Leptonica  1.82.0
Image processing and image analysis suite
tiffio.c
Go to the documentation of this file.
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
107 #ifdef HAVE_CONFIG_H
108 #include <config_auto.h>
109 #endif /* HAVE_CONFIG_H */
110 
111 #include <string.h>
112 #include <math.h> /* for isnan */
113 #include <sys/types.h>
114 #ifndef _MSC_VER
115 #include <unistd.h>
116 #else /* _MSC_VER */
117 #include <io.h>
118 #endif /* _MSC_VER */
119 #include <fcntl.h>
120 #include "allheaders.h"
121 
122 /* --------------------------------------------*/
123 #if HAVE_LIBTIFF /* defined in environ.h */
124 /* --------------------------------------------*/
125 
126 #include "tiff.h"
127 #include "tiffio.h"
128 
129 static const l_int32 DefaultResolution = 300; /* ppi */
130 static const l_int32 ManyPagesInTiffFile = 3000; /* warn if big */
131 
132  /* Verified that tiflib makes valid g4 files of this size */
133 static const l_int32 MaxTiffWidth = 1 << 20; /* 1M pixels */
134 static const l_int32 MaxTiffHeight = 1 << 20; /* 1M pixels */
135 
136  /* Check g4 data size */
137 static const size_t MaxNumTiffBytes = (1 << 28) - 1; /* 256 MB */
138 
139  /* All functions with TIFF interfaces are static. */
140 static PIX *pixReadFromTiffStream(TIFF *tif);
141 static l_int32 getTiffStreamResolution(TIFF *tif, l_int32 *pxres,
142  l_int32 *pyres);
143 static l_int32 tiffReadHeaderTiff(TIFF *tif, l_int32 *pwidth,
144  l_int32 *pheight, l_int32 *pbps,
145  l_int32 *pspp, l_int32 *pres,
146  l_int32 *pcmap, l_int32 *pformat);
147 static l_int32 writeCustomTiffTags(TIFF *tif, NUMA *natags,
148  SARRAY *savals, SARRAY *satypes,
149  NUMA *nasizes);
150 static l_int32 pixWriteToTiffStream(TIFF *tif, PIX *pix, l_int32 comptype,
151  NUMA *natags, SARRAY *savals,
152  SARRAY *satypes, NUMA *nasizes);
153 static TIFF *fopenTiff(FILE *fp, const char *modestring);
154 static TIFF *openTiff(const char *filename, const char *modestring);
155 
156  /* Static helper for tiff compression type */
157 static l_int32 getTiffCompressedFormat(l_uint16 tiffcomp);
158 
159  /* Static function for memory I/O */
160 static TIFF *fopenTiffMemstream(const char *filename, const char *operation,
161  l_uint8 **pdata, size_t *pdatasize);
162 
163  /* This structure defines a transform to be performed on a TIFF image
164  * (note that the same transformation can be represented in
165  * several different ways using this structure since
166  * vflip + hflip + counterclockwise == clockwise). */
168  int vflip; /* if non-zero, image needs a vertical fip */
169  int hflip; /* if non-zero, image needs a horizontal flip */
170  int rotate; /* -1 -> counterclockwise 90-degree rotation,
171  0 -> no rotation
172  1 -> clockwise 90-degree rotation */
173 };
174 
175  /* This describes the transformations needed for a given orientation
176  * tag. The tag values start at 1, so you need to subtract 1 to get a
177  * valid index into this array. It is only valid when not using
178  * TIFFReadRGBAImageOriented(). */
179 static struct tiff_transform tiff_orientation_transforms[] = {
180  {0, 0, 0},
181  {0, 1, 0},
182  {1, 1, 0},
183  {1, 0, 0},
184  {0, 1, -1},
185  {0, 0, 1},
186  {0, 1, 1},
187  {0, 0, -1}
188 };
189 
190  /* Same as above, except that test transformations are only valid
191  * when using TIFFReadRGBAImageOriented(). Transformations
192  * were determined empirically. See the libtiff mailing list for
193  * more discussion: http://www.asmail.be/msg0054683875.html */
194 static struct tiff_transform tiff_partial_orientation_transforms[] = {
195  {0, 0, 0},
196  {0, 0, 0},
197  {0, 0, 0},
198  {0, 0, 0},
199  {0, 1, -1},
200  {0, 1, 1},
201  {1, 0, 1},
202  {0, 1, -1}
203 };
204 
205 
206 /*-----------------------------------------------------------------------*
207  * TIFFClientOpen() wrappers for FILE* *
208  * Provided by Jürgen Buchmüller *
209  * *
210  * We previously used TIFFFdOpen(), which used low-level file *
211  * descriptors. It had portability issues with Windows, along *
212  * with other limitations from lack of stream control operations. *
213  * These callbacks to TIFFClientOpen() avoid the problems. *
214  * *
215  * Jürgen made the functions use 64 bit file operations where possible *
216  * or required, namely for seek and size. On Windows there are specific *
217  * _fseeki64() and _ftelli64() functions. On unix it is common to look *
218  * for a macro _LARGEFILE64_SOURCE being defined, which makes available *
219  * the off64_t type, and to use fseeko() and ftello() in this case. *
220  *-----------------------------------------------------------------------*/
221 static tsize_t
222 lept_read_proc(thandle_t cookie,
223  tdata_t buff,
224  tsize_t size)
225 {
226  FILE* fp = (FILE *)cookie;
227  tsize_t done;
228  if (!buff || !cookie || !fp)
229  return (tsize_t)-1;
230  done = fread(buff, 1, size, fp);
231  return done;
232 }
233 
234 static tsize_t
235 lept_write_proc(thandle_t cookie,
236  tdata_t buff,
237  tsize_t size)
238 {
239  FILE* fp = (FILE *)cookie;
240  tsize_t done;
241  if (!buff || !cookie || !fp)
242  return (tsize_t)-1;
243  done = fwrite(buff, 1, size, fp);
244  return done;
245 }
246 
247 static toff_t
248 lept_seek_proc(thandle_t cookie,
249  toff_t offs,
250  int whence)
251 {
252  FILE* fp = (FILE *)cookie;
253 #if defined(_MSC_VER)
254  __int64 pos = 0;
255  if (!cookie || !fp)
256  return (tsize_t)-1;
257  switch (whence) {
258  case SEEK_SET:
259  pos = 0;
260  break;
261  case SEEK_CUR:
262  pos = ftell(fp);
263  break;
264  case SEEK_END:
265  _fseeki64(fp, 0, SEEK_END);
266  pos = _ftelli64(fp);
267  break;
268  }
269  pos = (__int64)(pos + offs);
270  _fseeki64(fp, pos, SEEK_SET);
271  if (pos == _ftelli64(fp))
272  return (tsize_t)pos;
273 #elif defined(_LARGEFILE64_SOURCE)
274  off64_t pos = 0;
275  if (!cookie || !fp)
276  return (tsize_t)-1;
277  switch (whence) {
278  case SEEK_SET:
279  pos = 0;
280  break;
281  case SEEK_CUR:
282  pos = ftello(fp);
283  break;
284  case SEEK_END:
285  fseeko(fp, 0, SEEK_END);
286  pos = ftello(fp);
287  break;
288  }
289  pos = (off64_t)(pos + offs);
290  fseeko(fp, pos, SEEK_SET);
291  if (pos == ftello(fp))
292  return (tsize_t)pos;
293 #else
294  off_t pos = 0;
295  if (!cookie || !fp)
296  return (tsize_t)-1;
297  switch (whence) {
298  case SEEK_SET:
299  pos = 0;
300  break;
301  case SEEK_CUR:
302  pos = ftell(fp);
303  break;
304  case SEEK_END:
305  fseek(fp, 0, SEEK_END);
306  pos = ftell(fp);
307  break;
308  }
309  pos = (off_t)(pos + offs);
310  fseek(fp, pos, SEEK_SET);
311  if (pos == ftell(fp))
312  return (tsize_t)pos;
313 #endif
314  return (tsize_t)-1;
315 }
316 
317 static int
318 lept_close_proc(thandle_t cookie)
319 {
320  FILE* fp = (FILE *)cookie;
321  if (!cookie || !fp)
322  return 0;
323  fseek(fp, 0, SEEK_SET);
324  return 0;
325 }
326 
327 static toff_t
328 lept_size_proc(thandle_t cookie)
329 {
330  FILE* fp = (FILE *)cookie;
331 #if defined(_MSC_VER)
332  __int64 pos;
333  __int64 size;
334  if (!cookie || !fp)
335  return (tsize_t)-1;
336  pos = _ftelli64(fp);
337  _fseeki64(fp, 0, SEEK_END);
338  size = _ftelli64(fp);
339  _fseeki64(fp, pos, SEEK_SET);
340 #elif defined(_LARGEFILE64_SOURCE)
341  off64_t pos;
342  off64_t size;
343  if (!fp)
344  return (tsize_t)-1;
345  pos = ftello(fp);
346  fseeko(fp, 0, SEEK_END);
347  size = ftello(fp);
348  fseeko(fp, pos, SEEK_SET);
349 #else
350  off_t pos;
351  off_t size;
352  if (!cookie || !fp)
353  return (tsize_t)-1;
354  pos = ftell(fp);
355  fseek(fp, 0, SEEK_END);
356  size = ftell(fp);
357  fseek(fp, pos, SEEK_SET);
358 #endif
359  return (toff_t)size;
360 }
361 
362 
363 /*--------------------------------------------------------------*
364  * Reading from file *
365  *--------------------------------------------------------------*/
382 PIX *
383 pixReadTiff(const char *filename,
384  l_int32 n)
385 {
386 FILE *fp;
387 PIX *pix;
388 
389  PROCNAME("pixReadTiff");
390 
391  if (!filename)
392  return (PIX *)ERROR_PTR("filename not defined", procName, NULL);
393 
394  if ((fp = fopenReadStream(filename)) == NULL)
395  return (PIX *)ERROR_PTR("image file not found", procName, NULL);
396  pix = pixReadStreamTiff(fp, n);
397  fclose(fp);
398  return pix;
399 }
400 
401 
402 /*--------------------------------------------------------------*
403  * Reading from stream *
404  *--------------------------------------------------------------*/
419 PIX *
421  l_int32 n)
422 {
423 PIX *pix;
424 TIFF *tif;
425 
426  PROCNAME("pixReadStreamTiff");
427 
428  if (!fp)
429  return (PIX *)ERROR_PTR("stream not defined", procName, NULL);
430 
431  if ((tif = fopenTiff(fp, "r")) == NULL)
432  return (PIX *)ERROR_PTR("tif not opened", procName, NULL);
433 
434  if (TIFFSetDirectory(tif, n) == 0) {
435  TIFFCleanup(tif);
436  return NULL;
437  }
438  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
439  TIFFCleanup(tif);
440  return NULL;
441  }
442  TIFFCleanup(tif);
443  return pix;
444 }
445 
446 
484 static PIX *
486 {
487 char *text;
488 l_uint8 *linebuf, *data, *rowptr;
489 l_uint16 spp, bps, photometry, tiffcomp, orientation, sample_fmt;
490 l_uint16 *redmap, *greenmap, *bluemap;
491 l_int32 d, wpl, bpl, comptype, i, j, k, ncolors, rval, gval, bval, aval;
492 l_int32 xres, yres, tiffbpl, packedbpl, half_size, twothirds_size;
493 l_uint32 w, h, tiffword, read_oriented;
494 l_uint32 *line, *ppixel, *tiffdata, *pixdata;
495 PIX *pix, *pix1;
496 PIXCMAP *cmap;
497 
498  PROCNAME("pixReadFromTiffStream");
499 
500  if (!tif)
501  return (PIX *)ERROR_PTR("tif not defined", procName, NULL);
502 
503  read_oriented = 0;
504 
505  /* Only accept uint image data:
506  * SAMPLEFORMAT_UINT = 1;
507  * SAMPLEFORMAT_INT = 2;
508  * SAMPLEFORMAT_IEEEFP = 3;
509  * SAMPLEFORMAT_VOID = 4; */
510  TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_fmt);
511  if (sample_fmt != SAMPLEFORMAT_UINT) {
512  L_ERROR("sample format = %d is not uint\n", procName, sample_fmt);
513  return NULL;
514  }
515 
516  /* Can't read tiff in tiled format. For what is involved, see, e.g:
517  * https://www.cs.rochester.edu/~nelson/courses/vision/\
518  * resources/tiff/libtiff.html#Tiles
519  * A tiled tiff can be converted to a normal (strip) tif:
520  * tiffcp -s <input-tiled-tif> <output-strip-tif> */
521  if (TIFFIsTiled(tif)) {
522  L_ERROR("tiled format is not supported\n", procName);
523  return NULL;
524  }
525 
526  /* Old style jpeg is not supported. We tried supporting 8 bpp.
527  * TIFFReadScanline() fails on this format, so we used RGBA
528  * reading, which generates a 4 spp image, and pulled out the
529  * red component. However, there were problems with double-frees
530  * in cleanup. For RGB, tiffbpl is exactly half the size that
531  * you would expect for the raster data in a scanline, which
532  * is 3 * w. */
533  TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp);
534  if (tiffcomp == COMPRESSION_OJPEG) {
535  L_ERROR("old style jpeg format is not supported\n", procName);
536  return NULL;
537  }
538 
539  /* webp in tiff is in 4.1.0 and not yet supported in Adobe registry */
540 #if defined(COMPRESSION_WEBP)
541  if (tiffcomp == COMPRESSION_WEBP) {
542  L_ERROR("webp in tiff not generally supported yet\n", procName);
543  return NULL;
544  }
545 #endif /* COMPRESSION_WEBP */
546 
547  /* Use default fields for bps and spp */
548  TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
549  TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
550  if (bps != 1 && bps != 2 && bps != 4 && bps != 8 && bps != 16) {
551  L_ERROR("invalid bps = %d\n", procName, bps);
552  return NULL;
553  }
554  if (spp == 2 && bps != 8) {
555  L_WARNING("for 2 spp, only handle 8 bps\n", procName);
556  return NULL;
557  }
558  if (spp == 1) {
559  d = bps;
560  } else if (spp == 2) { /* gray plus alpha */
561  d = 32; /* will convert to RGBA */
562  } else if (spp == 3 || spp == 4) {
563  d = 32;
564  } else {
565  L_ERROR("spp = %d; not in {1,2,3,4}\n", procName, spp);
566  return NULL;
567  }
568 
569  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
570  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
571  if (w > MaxTiffWidth) {
572  L_ERROR("width = %d pixels; too large\n", procName, w);
573  return NULL;
574  }
575  if (h > MaxTiffHeight) {
576  L_ERROR("height = %d pixels; too large\n", procName, h);
577  return NULL;
578  }
579 
580  /* The relation between the size of a byte buffer required to hold
581  a raster of image pixels (packedbpl) and the size of the tiff
582  buffer (tiffbuf) is either 1:1 or approximately 1.5:1 or 2:1,
583  depending on how the data is stored and subsampled. For security,
584  we test this relation between tiffbuf and the image parameters
585  w, spp and bps. */
586  tiffbpl = TIFFScanlineSize(tif);
587  packedbpl = (bps * spp * w + 7) / 8;
588  half_size = (L_ABS(2 * tiffbpl - packedbpl) <= 8);
589  twothirds_size = (L_ABS(3 * tiffbpl - 2 * packedbpl) <= 8);
590 #if 0
591  if (half_size)
592  L_INFO("half_size: packedbpl = %d is approx. twice tiffbpl = %d\n",
593  procName, packedbpl, tiffbpl);
594  if (twothirds_size)
595  L_INFO("twothirds_size: packedbpl = %d is approx. 1.5 tiffbpl = %d\n",
596  procName, packedbpl, tiffbpl);
597  lept_stderr("tiffbpl = %d, packedbpl = %d, bps = %d, spp = %d, w = %d\n",
598  tiffbpl, packedbpl, bps, spp, w);
599 #endif
600  if (tiffbpl != packedbpl && !half_size && !twothirds_size) {
601  L_ERROR("invalid tiffbpl: tiffbpl = %d, packedbpl = %d, "
602  "bps = %d, spp = %d, w = %d\n",
603  procName, tiffbpl, packedbpl, bps, spp, w);
604  return NULL;
605  }
606 
607  /* Use a linebuf that will hold all the pixels generated
608  by tiff when reading (decompressing) a scanline. */
609  if ((pix = pixCreate(w, h, d)) == NULL)
610  return (PIX *)ERROR_PTR("pix not made", procName, NULL);
611  pixSetInputFormat(pix, IFF_TIFF);
612  data = (l_uint8 *)pixGetData(pix);
613  wpl = pixGetWpl(pix);
614  bpl = 4 * wpl;
615  if (spp == 1) {
616  linebuf = (l_uint8 *)LEPT_CALLOC(4 * wpl, sizeof(l_uint8));
617  for (i = 0; i < h; i++) {
618  if (TIFFReadScanline(tif, linebuf, i, 0) < 0) {
619  LEPT_FREE(linebuf);
620  pixDestroy(&pix);
621  L_ERROR("spp = 1, read fail at line %d\n", procName, i);
622  return NULL;
623  }
624  memcpy(data, linebuf, tiffbpl);
625  data += bpl;
626  }
627  if (bps <= 8)
628  pixEndianByteSwap(pix);
629  else /* bps == 16 */
631  LEPT_FREE(linebuf);
632  } else if (spp == 2 && bps == 8) { /* gray plus alpha */
633  L_INFO("gray+alpha is not supported; converting to RGBA\n", procName);
634  pixSetSpp(pix, 4);
635  linebuf = (l_uint8 *)LEPT_CALLOC(4 * wpl, sizeof(l_uint8));
636  pixdata = pixGetData(pix);
637  for (i = 0; i < h; i++) {
638  if (TIFFReadScanline(tif, linebuf, i, 0) < 0) {
639  LEPT_FREE(linebuf);
640  pixDestroy(&pix);
641  L_ERROR("spp = 2, read fail at line %d\n", procName, i);
642  return NULL;
643  }
644  rowptr = linebuf;
645  ppixel = pixdata + i * wpl;
646  for (j = k = 0; j < w; j++) {
647  /* Copy gray value into r, g and b */
648  SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
649  SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
650  SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
651  SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
652  ppixel++;
653  }
654  }
655  LEPT_FREE(linebuf);
656  } else { /* rgb and rgba */
657  if ((tiffdata = (l_uint32 *)LEPT_CALLOC((size_t)w * h,
658  sizeof(l_uint32))) == NULL) {
659  pixDestroy(&pix);
660  return (PIX *)ERROR_PTR("calloc fail for tiffdata", procName, NULL);
661  }
662  /* TIFFReadRGBAImageOriented() converts to 8 bps */
663  if (!TIFFReadRGBAImageOriented(tif, w, h, tiffdata,
664  ORIENTATION_TOPLEFT, 0)) {
665  LEPT_FREE(tiffdata);
666  pixDestroy(&pix);
667  return (PIX *)ERROR_PTR("failed to read tiffdata", procName, NULL);
668  } else {
669  read_oriented = 1;
670  }
671 
672  if (spp == 4) pixSetSpp(pix, 4);
673  line = pixGetData(pix);
674  for (i = 0; i < h; i++, line += wpl) {
675  for (j = 0, ppixel = line; j < w; j++) {
676  /* TIFFGet* are macros */
677  tiffword = tiffdata[i * w + j];
678  rval = TIFFGetR(tiffword);
679  gval = TIFFGetG(tiffword);
680  bval = TIFFGetB(tiffword);
681  if (spp == 3) {
682  composeRGBPixel(rval, gval, bval, ppixel);
683  } else { /* spp == 4 */
684  aval = TIFFGetA(tiffword);
685  composeRGBAPixel(rval, gval, bval, aval, ppixel);
686  }
687  ppixel++;
688  }
689  }
690  LEPT_FREE(tiffdata);
691  }
692 
693  if (getTiffStreamResolution(tif, &xres, &yres) == 0) {
694  pixSetXRes(pix, xres);
695  pixSetYRes(pix, yres);
696  }
697 
698  /* Find and save the compression type */
699  comptype = getTiffCompressedFormat(tiffcomp);
700  pixSetInputFormat(pix, comptype);
701 
702  if (TIFFGetField(tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap)) {
703  /* Save the colormap as a pix cmap. Because the
704  * tiff colormap components are 16 bit unsigned,
705  * and go from black (0) to white (0xffff), the
706  * the pix cmap takes the most significant byte. */
707  if (bps > 8) {
708  pixDestroy(&pix);
709  return (PIX *)ERROR_PTR("colormap size > 256", procName, NULL);
710  }
711  if ((cmap = pixcmapCreate(bps)) == NULL) {
712  pixDestroy(&pix);
713  return (PIX *)ERROR_PTR("colormap not made", procName, NULL);
714  }
715  ncolors = 1 << bps;
716  for (i = 0; i < ncolors; i++)
717  pixcmapAddColor(cmap, redmap[i] >> 8, greenmap[i] >> 8,
718  bluemap[i] >> 8);
719  if (pixSetColormap(pix, cmap)) {
720  pixDestroy(&pix);
721  return (PIX *)ERROR_PTR("invalid colormap", procName, NULL);
722  }
723 
724  /* Remove the colormap for 1 bpp. */
725  if (bps == 1) {
727  pixDestroy(&pix);
728  pix = pix1;
729  }
730  } else { /* No colormap: check photometry and invert if necessary */
731  if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometry)) {
732  /* Guess default photometry setting. Assume min_is_white
733  * if compressed 1 bpp; min_is_black otherwise. */
734  if (tiffcomp == COMPRESSION_CCITTFAX3 ||
735  tiffcomp == COMPRESSION_CCITTFAX4 ||
736  tiffcomp == COMPRESSION_CCITTRLE ||
737  tiffcomp == COMPRESSION_CCITTRLEW) {
738  photometry = PHOTOMETRIC_MINISWHITE;
739  } else {
740  photometry = PHOTOMETRIC_MINISBLACK;
741  }
742  }
743  if ((d == 1 && photometry == PHOTOMETRIC_MINISBLACK) ||
744  (d == 8 && photometry == PHOTOMETRIC_MINISWHITE))
745  pixInvert(pix, pix);
746  }
747 
748  if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation)) {
749  if (orientation >= 1 && orientation <= 8) {
750  struct tiff_transform *transform = (read_oriented) ?
751  &tiff_partial_orientation_transforms[orientation - 1] :
752  &tiff_orientation_transforms[orientation - 1];
753  if (transform->vflip) pixFlipTB(pix, pix);
754  if (transform->hflip) pixFlipLR(pix, pix);
755  if (transform->rotate) {
756  PIX *oldpix = pix;
757  pix = pixRotate90(oldpix, transform->rotate);
758  pixDestroy(&oldpix);
759  }
760  }
761  }
762 
763  text = NULL;
764  TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &text);
765  if (text) pixSetText(pix, text);
766  return pix;
767 }
768 
769 
770 /*--------------------------------------------------------------*
771  * Writing to file *
772  *--------------------------------------------------------------*/
794 l_ok
795 pixWriteTiff(const char *filename,
796  PIX *pix,
797  l_int32 comptype,
798  const char *modestr)
799 {
800  return pixWriteTiffCustom(filename, pix, comptype, modestr,
801  NULL, NULL, NULL, NULL);
802 }
803 
804 
851 l_ok
852 pixWriteTiffCustom(const char *filename,
853  PIX *pix,
854  l_int32 comptype,
855  const char *modestr,
856  NUMA *natags,
857  SARRAY *savals,
858  SARRAY *satypes,
859  NUMA *nasizes)
860 {
861 l_int32 ret;
862 TIFF *tif;
863 
864  PROCNAME("pixWriteTiffCustom");
865 
866  if (!filename)
867  return ERROR_INT("filename not defined", procName, 1);
868  if (!pix)
869  return ERROR_INT("pix not defined", procName, 1);
870 
871  if ((tif = openTiff(filename, modestr)) == NULL)
872  return ERROR_INT("tif not opened", procName, 1);
873  ret = pixWriteToTiffStream(tif, pix, comptype, natags, savals,
874  satypes, nasizes);
875  TIFFClose(tif);
876  return ret;
877 }
878 
879 
880 /*--------------------------------------------------------------*
881  * Writing to stream *
882  *--------------------------------------------------------------*/
910 l_ok
912  PIX *pix,
913  l_int32 comptype)
914 {
915  return pixWriteStreamTiffWA(fp, pix, comptype, "w");
916 }
917 
918 
935 l_ok
937  PIX *pix,
938  l_int32 comptype,
939  const char *modestr)
940 {
941 TIFF *tif;
942 
943  PROCNAME("pixWriteStreamTiffWA");
944 
945  if (!fp)
946  return ERROR_INT("stream not defined", procName, 1 );
947  if (!pix)
948  return ERROR_INT("pix not defined", procName, 1 );
949  if (strcmp(modestr, "w") && strcmp(modestr, "a")) {
950  L_ERROR("modestr = %s; not 'w' or 'a'\n", procName, modestr);
951  return 1;
952  }
953 
954  if (pixGetDepth(pix) != 1 && comptype != IFF_TIFF &&
955  comptype != IFF_TIFF_LZW && comptype != IFF_TIFF_ZIP &&
956  comptype != IFF_TIFF_JPEG) {
957  L_WARNING("invalid compression type %d for bpp > 1; using TIFF_ZIP\n",
958  procName, comptype);
959  comptype = IFF_TIFF_ZIP;
960  }
961 
962  if ((tif = fopenTiff(fp, modestr)) == NULL)
963  return ERROR_INT("tif not opened", procName, 1);
964 
965  if (pixWriteToTiffStream(tif, pix, comptype, NULL, NULL, NULL, NULL)) {
966  TIFFCleanup(tif);
967  return ERROR_INT("tif write error", procName, 1);
968  }
969 
970  TIFFCleanup(tif);
971  return 0;
972 }
973 
974 
1010 static l_int32
1012  PIX *pix,
1013  l_int32 comptype,
1014  NUMA *natags,
1015  SARRAY *savals,
1016  SARRAY *satypes,
1017  NUMA *nasizes)
1018 {
1019 l_uint8 *linebuf, *data;
1020 l_uint16 redmap[256], greenmap[256], bluemap[256];
1021 l_int32 w, h, d, spp, i, j, k, wpl, bpl, tiffbpl, ncolors, cmapsize;
1022 l_int32 *rmap, *gmap, *bmap;
1023 l_int32 xres, yres;
1024 l_uint32 *line, *ppixel;
1025 PIX *pixt;
1026 PIXCMAP *cmap;
1027 char *text;
1028 
1029  PROCNAME("pixWriteToTiffStream");
1030 
1031  if (!tif)
1032  return ERROR_INT("tif stream not defined", procName, 1);
1033  if (!pix)
1034  return ERROR_INT( "pix not defined", procName, 1 );
1035 
1036  pixSetPadBits(pix, 0);
1037  pixGetDimensions(pix, &w, &h, &d);
1038  spp = pixGetSpp(pix);
1039  xres = pixGetXRes(pix);
1040  yres = pixGetYRes(pix);
1041  if (xres == 0) xres = DefaultResolution;
1042  if (yres == 0) yres = DefaultResolution;
1043 
1044  /* ------------------ Write out the header ------------- */
1045  TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (l_uint32)RESUNIT_INCH);
1046  TIFFSetField(tif, TIFFTAG_XRESOLUTION, (l_float64)xres);
1047  TIFFSetField(tif, TIFFTAG_YRESOLUTION, (l_float64)yres);
1048 
1049  TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (l_uint32)w);
1050  TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (l_uint32)h);
1051  TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
1052 
1053  if ((text = pixGetText(pix)) != NULL)
1054  TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, text);
1055 
1056  if (d == 1 && !pixGetColormap(pix)) {
1057  /* If d == 1, preserve the colormap. Note that when
1058  * d == 1 pix with colormaps are read, the colormaps
1059  * are removed. The only pix in leptonica that have
1060  * colormaps are made programmatically. */
1061  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
1062  } else if ((d == 32 && spp == 3) || d == 24) {
1063  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
1064  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)3);
1065  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,
1066  (l_uint16)8, (l_uint16)8, (l_uint16)8);
1067  } else if (d == 32 && spp == 4) {
1068  l_uint16 val[1];
1069  val[0] = EXTRASAMPLE_ASSOCALPHA;
1070  TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, (l_uint16)1, &val);
1071  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
1072  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)4);
1073  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,
1074  (l_uint16)8, (l_uint16)8, (l_uint16)8, (l_uint16)8);
1075  } else if (d == 16) { /* we only support spp = 1, bps = 16 */
1076  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
1077  } else if ((cmap = pixGetColormap(pix)) == NULL) {
1078  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
1079  } else { /* Save colormap in the tiff; not more than 256 colors */
1080  if (d > 8) {
1081  L_ERROR("d = %d > 8 with colormap!; reducing to 8\n", procName, d);
1082  d = 8;
1083  }
1084  pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL);
1085  ncolors = pixcmapGetCount(cmap);
1086  ncolors = L_MIN(256, ncolors); /* max 256 */
1087  cmapsize = 1 << d;
1088  cmapsize = L_MIN(256, cmapsize); /* power of 2; max 256 */
1089  if (ncolors > cmapsize) {
1090  L_WARNING("too many colors in cmap for tiff; truncating\n",
1091  procName);
1092  ncolors = cmapsize;
1093  }
1094  for (i = 0; i < ncolors; i++) {
1095  redmap[i] = (rmap[i] << 8) | rmap[i];
1096  greenmap[i] = (gmap[i] << 8) | gmap[i];
1097  bluemap[i] = (bmap[i] << 8) | bmap[i];
1098  }
1099  for (i = ncolors; i < cmapsize; i++) /* init, even though not used */
1100  redmap[i] = greenmap[i] = bluemap[i] = 0;
1101  LEPT_FREE(rmap);
1102  LEPT_FREE(gmap);
1103  LEPT_FREE(bmap);
1104 
1105  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
1106  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)1);
1107  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (l_uint16)d);
1108  TIFFSetField(tif, TIFFTAG_COLORMAP, redmap, greenmap, bluemap);
1109  }
1110 
1111  if (d <= 16) {
1112  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (l_uint16)d);
1113  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)1);
1114  }
1115 
1116  TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
1117  if (comptype == IFF_TIFF) { /* no compression */
1118  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
1119  } else if (comptype == IFF_TIFF_G4) {
1120  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4);
1121  } else if (comptype == IFF_TIFF_G3) {
1122  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
1123  } else if (comptype == IFF_TIFF_RLE) {
1124  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
1125  } else if (comptype == IFF_TIFF_PACKBITS) {
1126  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
1127  } else if (comptype == IFF_TIFF_LZW) {
1128  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
1129  } else if (comptype == IFF_TIFF_ZIP) {
1130  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
1131  } else if (comptype == IFF_TIFF_JPEG) {
1132  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
1133  } else {
1134  L_WARNING("unknown tiff compression; using none\n", procName);
1135  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
1136  }
1137 
1138  /* This is a no-op if arrays are NULL */
1139  writeCustomTiffTags(tif, natags, savals, satypes, nasizes);
1140 
1141  /* ------------- Write out the image data ------------- */
1142  tiffbpl = TIFFScanlineSize(tif);
1143  wpl = pixGetWpl(pix);
1144  bpl = 4 * wpl;
1145  if (tiffbpl > bpl)
1146  lept_stderr("Big trouble: tiffbpl = %d, bpl = %d\n", tiffbpl, bpl);
1147  if ((linebuf = (l_uint8 *)LEPT_CALLOC(1, bpl)) == NULL)
1148  return ERROR_INT("calloc fail for linebuf", procName, 1);
1149 
1150  /* Use single strip for image */
1151  TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, h);
1152 
1153  if (d != 24 && d != 32) {
1154  if (d == 16)
1155  pixt = pixEndianTwoByteSwapNew(pix);
1156  else
1157  pixt = pixEndianByteSwapNew(pix);
1158  data = (l_uint8 *)pixGetData(pixt);
1159  for (i = 0; i < h; i++, data += bpl) {
1160  memcpy(linebuf, data, tiffbpl);
1161  if (TIFFWriteScanline(tif, linebuf, i, 0) < 0)
1162  break;
1163  }
1164  pixDestroy(&pixt);
1165  } else if (d == 24) { /* See note 4 above: special case of 24 bpp rgb */
1166  for (i = 0; i < h; i++) {
1167  line = pixGetData(pix) + i * wpl;
1168  if (TIFFWriteScanline(tif, (l_uint8 *)line, i, 0) < 0)
1169  break;
1170  }
1171  } else { /* 32 bpp rgb or rgba */
1172  for (i = 0; i < h; i++) {
1173  line = pixGetData(pix) + i * wpl;
1174  for (j = 0, k = 0, ppixel = line; j < w; j++) {
1175  linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
1176  linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
1177  linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
1178  if (spp == 4)
1179  linebuf[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
1180  ppixel++;
1181  }
1182  if (TIFFWriteScanline(tif, linebuf, i, 0) < 0)
1183  break;
1184  }
1185  }
1186 
1187 /* TIFFWriteDirectory(tif); */
1188  LEPT_FREE(linebuf);
1189 
1190  return 0;
1191 }
1192 
1193 
1223 static l_int32
1225  NUMA *natags,
1226  SARRAY *savals,
1227  SARRAY *satypes,
1228  NUMA *nasizes)
1229 {
1230 char *sval, *type;
1231 l_int32 i, n, ns, size, tagval, val;
1232 l_float64 dval;
1233 l_uint32 uval, uval2;
1234 
1235  PROCNAME("writeCustomTiffTags");
1236 
1237  if (!tif)
1238  return ERROR_INT("tif stream not defined", procName, 1);
1239  if (!natags && !savals && !satypes)
1240  return 0;
1241  if (!natags || !savals || !satypes)
1242  return ERROR_INT("not all arrays defined", procName, 1);
1243  n = numaGetCount(natags);
1244  if ((sarrayGetCount(savals) != n) || (sarrayGetCount(satypes) != n))
1245  return ERROR_INT("not all sa the same size", procName, 1);
1246 
1247  /* The sized arrays (4 args to TIFFSetField) are written first */
1248  if (nasizes) {
1249  ns = numaGetCount(nasizes);
1250  if (ns > n)
1251  return ERROR_INT("too many 4-arg tag calls", procName, 1);
1252  for (i = 0; i < ns; i++) {
1253  numaGetIValue(natags, i, &tagval);
1254  sval = sarrayGetString(savals, i, L_NOCOPY);
1255  type = sarrayGetString(satypes, i, L_NOCOPY);
1256  numaGetIValue(nasizes, i, &size);
1257  if (strcmp(type, "char*") && strcmp(type, "l_uint8*"))
1258  L_WARNING("array type not char* or l_uint8*; ignore\n",
1259  procName);
1260  TIFFSetField(tif, tagval, size, sval);
1261  }
1262  } else {
1263  ns = 0;
1264  }
1265 
1266  /* The typical tags (3 args to TIFFSetField) are now written */
1267  for (i = ns; i < n; i++) {
1268  numaGetIValue(natags, i, &tagval);
1269  sval = sarrayGetString(savals, i, L_NOCOPY);
1270  type = sarrayGetString(satypes, i, L_NOCOPY);
1271  if (!strcmp(type, "char*") || !strcmp(type, "const char*")) {
1272  TIFFSetField(tif, tagval, sval);
1273  } else if (!strcmp(type, "l_uint16")) {
1274  if (sscanf(sval, "%u", &uval) == 1) {
1275  TIFFSetField(tif, tagval, (l_uint16)uval);
1276  } else {
1277  lept_stderr("val %s not of type %s\n", sval, type);
1278  return ERROR_INT("custom tag(s) not written", procName, 1);
1279  }
1280  } else if (!strcmp(type, "l_uint32")) {
1281  if (sscanf(sval, "%u", &uval) == 1) {
1282  TIFFSetField(tif, tagval, uval);
1283  } else {
1284  lept_stderr("val %s not of type %s\n", sval, type);
1285  return ERROR_INT("custom tag(s) not written", procName, 1);
1286  }
1287  } else if (!strcmp(type, "l_int32")) {
1288  if (sscanf(sval, "%d", &val) == 1) {
1289  TIFFSetField(tif, tagval, val);
1290  } else {
1291  lept_stderr("val %s not of type %s\n", sval, type);
1292  return ERROR_INT("custom tag(s) not written", procName, 1);
1293  }
1294  } else if (!strcmp(type, "l_float64")) {
1295  if (sscanf(sval, "%lf", &dval) == 1) {
1296  TIFFSetField(tif, tagval, dval);
1297  } else {
1298  lept_stderr("val %s not of type %s\n", sval, type);
1299  return ERROR_INT("custom tag(s) not written", procName, 1);
1300  }
1301  } else if (!strcmp(type, "l_uint16-l_uint16")) {
1302  if (sscanf(sval, "%u-%u", &uval, &uval2) == 2) {
1303  TIFFSetField(tif, tagval, (l_uint16)uval, (l_uint16)uval2);
1304  } else {
1305  lept_stderr("val %s not of type %s\n", sval, type);
1306  return ERROR_INT("custom tag(s) not written", procName, 1);
1307  }
1308  } else {
1309  lept_stderr("unknown type %s\n",type);
1310  return ERROR_INT("unknown type; tag(s) not written", procName, 1);
1311  }
1312  }
1313  return 0;
1314 }
1315 
1316 
1317 /*--------------------------------------------------------------*
1318  * Reading and writing multipage tiff *
1319  *--------------------------------------------------------------*/
1351 PIX *
1352 pixReadFromMultipageTiff(const char *fname,
1353  size_t *poffset)
1354 {
1355 l_int32 retval;
1356 size_t offset;
1357 PIX *pix;
1358 TIFF *tif;
1359 
1360  PROCNAME("pixReadFromMultipageTiff");
1361 
1362  if (!fname)
1363  return (PIX *)ERROR_PTR("fname not defined", procName, NULL);
1364  if (!poffset)
1365  return (PIX *)ERROR_PTR("&offset not defined", procName, NULL);
1366 
1367  if ((tif = openTiff(fname, "r")) == NULL) {
1368  L_ERROR("tif open failed for %s\n", procName, fname);
1369  return NULL;
1370  }
1371 
1372  /* Set ptrs in the TIFF to the beginning of the image */
1373  offset = *poffset;
1374  retval = (offset == 0) ? TIFFSetDirectory(tif, 0)
1375  : TIFFSetSubDirectory(tif, offset);
1376  if (retval == 0) {
1377  TIFFClose(tif);
1378  return NULL;
1379  }
1380 
1381  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
1382  TIFFClose(tif);
1383  return NULL;
1384  }
1385 
1386  /* Advance to the next image and return the new offset */
1387  TIFFReadDirectory(tif);
1388  *poffset = TIFFCurrentDirOffset(tif);
1389  TIFFClose(tif);
1390  return pix;
1391 }
1392 
1393 
1400 PIXA *
1401 pixaReadMultipageTiff(const char *filename)
1402 {
1403 l_int32 i, npages;
1404 FILE *fp;
1405 PIX *pix;
1406 PIXA *pixa;
1407 TIFF *tif;
1408 
1409  PROCNAME("pixaReadMultipageTiff");
1410 
1411  if (!filename)
1412  return (PIXA *)ERROR_PTR("filename not defined", procName, NULL);
1413 
1414  if ((fp = fopenReadStream(filename)) == NULL)
1415  return (PIXA *)ERROR_PTR("stream not opened", procName, NULL);
1416  if (fileFormatIsTiff(fp)) {
1417  tiffGetCount(fp, &npages);
1418  L_INFO(" Tiff: %d pages\n", procName, npages);
1419  } else {
1420  return (PIXA *)ERROR_PTR("file not tiff", procName, NULL);
1421  }
1422 
1423  if ((tif = fopenTiff(fp, "r")) == NULL)
1424  return (PIXA *)ERROR_PTR("tif not opened", procName, NULL);
1425 
1426  pixa = pixaCreate(npages);
1427  pix = NULL;
1428  for (i = 0; i < npages; i++) {
1429  if ((pix = pixReadFromTiffStream(tif)) != NULL) {
1430  pixaAddPix(pixa, pix, L_INSERT);
1431  } else {
1432  L_WARNING("pix not read for page %d\n", procName, i);
1433  }
1434 
1435  /* Advance to the next directory (i.e., the next image) */
1436  if (TIFFReadDirectory(tif) == 0)
1437  break;
1438  }
1439 
1440  fclose(fp);
1441  TIFFCleanup(tif);
1442  return pixa;
1443 }
1444 
1445 
1460 l_ok
1461 pixaWriteMultipageTiff(const char *fname,
1462  PIXA *pixa)
1463 {
1464 const char *modestr;
1465 l_int32 i, n;
1466 PIX *pix1;
1467 
1468  PROCNAME("pixaWriteMultipageTiff");
1469 
1470  if (!fname)
1471  return ERROR_INT("fname not defined", procName, 1);
1472  if (!pixa)
1473  return ERROR_INT("pixa not defined", procName, 1);
1474 
1475  n = pixaGetCount(pixa);
1476  for (i = 0; i < n; i++) {
1477  modestr = (i == 0) ? "w" : "a";
1478  pix1 = pixaGetPix(pixa, i, L_CLONE);
1479  if (pixGetDepth(pix1) == 1)
1480  pixWriteTiff(fname, pix1, IFF_TIFF_G4, modestr);
1481  else
1482  pixWriteTiff(fname, pix1, IFF_TIFF_ZIP, modestr);
1483  pixDestroy(&pix1);
1484  }
1485 
1486  return 0;
1487 }
1488 
1489 
1514 l_ok
1515 writeMultipageTiff(const char *dirin,
1516  const char *substr,
1517  const char *fileout)
1518 {
1519 SARRAY *sa;
1520 
1521  PROCNAME("writeMultipageTiff");
1522 
1523  if (!dirin)
1524  return ERROR_INT("dirin not defined", procName, 1);
1525  if (!fileout)
1526  return ERROR_INT("fileout not defined", procName, 1);
1527 
1528  /* Get all filtered and sorted full pathnames. */
1529  sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0);
1530 
1531  /* Generate the tiff file */
1532  writeMultipageTiffSA(sa, fileout);
1533  sarrayDestroy(&sa);
1534  return 0;
1535 }
1536 
1537 
1550 l_ok
1552  const char *fileout)
1553 {
1554 char *fname;
1555 const char *op;
1556 l_int32 i, nfiles, firstfile, format;
1557 PIX *pix;
1558 
1559  PROCNAME("writeMultipageTiffSA");
1560 
1561  if (!sa)
1562  return ERROR_INT("sa not defined", procName, 1);
1563  if (!fileout)
1564  return ERROR_INT("fileout not defined", procName, 1);
1565 
1566  nfiles = sarrayGetCount(sa);
1567  firstfile = TRUE;
1568  for (i = 0; i < nfiles; i++) {
1569  op = (firstfile) ? "w" : "a";
1570  fname = sarrayGetString(sa, i, L_NOCOPY);
1571  findFileFormat(fname, &format);
1572  if (format == IFF_UNKNOWN) {
1573  L_INFO("format of %s not known\n", procName, fname);
1574  continue;
1575  }
1576 
1577  if ((pix = pixRead(fname)) == NULL) {
1578  L_WARNING("pix not made for file: %s\n", procName, fname);
1579  continue;
1580  }
1581  if (pixGetDepth(pix) == 1)
1582  pixWriteTiff(fileout, pix, IFF_TIFF_G4, op);
1583  else
1584  pixWriteTiff(fileout, pix, IFF_TIFF_ZIP, op);
1585  firstfile = FALSE;
1586  pixDestroy(&pix);
1587  }
1588 
1589  return 0;
1590 }
1591 
1592 
1593 /*--------------------------------------------------------------*
1594  * Print info to stream *
1595  *--------------------------------------------------------------*/
1603 l_ok
1604 fprintTiffInfo(FILE *fpout,
1605  const char *tiffile)
1606 {
1607 TIFF *tif;
1608 
1609  PROCNAME("fprintTiffInfo");
1610 
1611  if (!tiffile)
1612  return ERROR_INT("tiffile not defined", procName, 1);
1613  if (!fpout)
1614  return ERROR_INT("stream out not defined", procName, 1);
1615 
1616  if ((tif = openTiff(tiffile, "rb")) == NULL)
1617  return ERROR_INT("tif not open for read", procName, 1);
1618 
1619  TIFFPrintDirectory(tif, fpout, 0);
1620  TIFFClose(tif);
1621 
1622  return 0;
1623 }
1624 
1625 
1626 /*--------------------------------------------------------------*
1627  * Get page count *
1628  *--------------------------------------------------------------*/
1636 l_ok
1637 tiffGetCount(FILE *fp,
1638  l_int32 *pn)
1639 {
1640 l_int32 i;
1641 TIFF *tif;
1642 
1643  PROCNAME("tiffGetCount");
1644 
1645  if (!fp)
1646  return ERROR_INT("stream not defined", procName, 1);
1647  if (!pn)
1648  return ERROR_INT("&n not defined", procName, 1);
1649  *pn = 0;
1650 
1651  if ((tif = fopenTiff(fp, "r")) == NULL)
1652  return ERROR_INT("tif not open for read", procName, 1);
1653 
1654  for (i = 1; ; i++) {
1655  if (TIFFReadDirectory(tif) == 0)
1656  break;
1657  if (i == ManyPagesInTiffFile + 1) {
1658  L_WARNING("big file: more than %d pages\n", procName,
1659  ManyPagesInTiffFile);
1660  }
1661  }
1662  *pn = i;
1663  TIFFCleanup(tif);
1664  return 0;
1665 }
1666 
1667 
1668 /*--------------------------------------------------------------*
1669  * Get resolution from tif *
1670  *--------------------------------------------------------------*/
1684 l_ok
1686  l_int32 *pxres,
1687  l_int32 *pyres)
1688 {
1689 TIFF *tif;
1690 
1691  PROCNAME("getTiffResolution");
1692 
1693  if (!pxres || !pyres)
1694  return ERROR_INT("&xres and &yres not both defined", procName, 1);
1695  *pxres = *pyres = 0;
1696  if (!fp)
1697  return ERROR_INT("stream not opened", procName, 1);
1698 
1699  if ((tif = fopenTiff(fp, "r")) == NULL)
1700  return ERROR_INT("tif not open for read", procName, 1);
1701  getTiffStreamResolution(tif, pxres, pyres);
1702  TIFFCleanup(tif);
1703  return 0;
1704 }
1705 
1706 
1720 static l_int32
1722  l_int32 *pxres,
1723  l_int32 *pyres)
1724 {
1725 l_uint16 resunit;
1726 l_int32 foundxres, foundyres;
1727 l_float32 fxres, fyres;
1728 
1729  PROCNAME("getTiffStreamResolution");
1730 
1731  if (!tif)
1732  return ERROR_INT("tif not opened", procName, 1);
1733  if (!pxres || !pyres)
1734  return ERROR_INT("&xres and &yres not both defined", procName, 1);
1735  *pxres = *pyres = 0;
1736 
1737  TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
1738  foundxres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &fxres);
1739  foundyres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &fyres);
1740  if (!foundxres && !foundyres) return 1;
1741  if (isnan(fxres) || isnan(fyres)) return 1;
1742  if (!foundxres && foundyres)
1743  fxres = fyres;
1744  else if (foundxres && !foundyres)
1745  fyres = fxres;
1746 
1747  /* Avoid overflow into int32; set max fxres and fyres to 5 x 10^8 */
1748  if (fxres < 0 || fxres > (1L << 29) || fyres < 0 || fyres > (1L << 29))
1749  return ERROR_INT("fxres and/or fyres values are invalid", procName, 1);
1750 
1751  if (resunit == RESUNIT_CENTIMETER) { /* convert to ppi */
1752  *pxres = (l_int32)(2.54 * fxres + 0.5);
1753  *pyres = (l_int32)(2.54 * fyres + 0.5);
1754  } else {
1755  *pxres = (l_int32)fxres;
1756  *pyres = (l_int32)fyres;
1757  }
1758 
1759  return 0;
1760 }
1761 
1762 
1763 /*--------------------------------------------------------------*
1764  * Get some tiff header information *
1765  *--------------------------------------------------------------*/
1786 l_ok
1787 readHeaderTiff(const char *filename,
1788  l_int32 n,
1789  l_int32 *pw,
1790  l_int32 *ph,
1791  l_int32 *pbps,
1792  l_int32 *pspp,
1793  l_int32 *pres,
1794  l_int32 *pcmap,
1795  l_int32 *pformat)
1796 {
1797 l_int32 ret;
1798 FILE *fp;
1799 
1800  PROCNAME("readHeaderTiff");
1801 
1802  if (pw) *pw = 0;
1803  if (ph) *ph = 0;
1804  if (pbps) *pbps = 0;
1805  if (pspp) *pspp = 0;
1806  if (pres) *pres = 0;
1807  if (pcmap) *pcmap = 0;
1808  if (pformat) *pformat = 0;
1809  if (!filename)
1810  return ERROR_INT("filename not defined", procName, 1);
1811  if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat)
1812  return ERROR_INT("no results requested", procName, 1);
1813 
1814  if ((fp = fopenReadStream(filename)) == NULL)
1815  return ERROR_INT("image file not found", procName, 1);
1816  ret = freadHeaderTiff(fp, n, pw, ph, pbps, pspp, pres, pcmap, pformat);
1817  fclose(fp);
1818  return ret;
1819 }
1820 
1821 
1842 l_ok
1844  l_int32 n,
1845  l_int32 *pw,
1846  l_int32 *ph,
1847  l_int32 *pbps,
1848  l_int32 *pspp,
1849  l_int32 *pres,
1850  l_int32 *pcmap,
1851  l_int32 *pformat)
1852 {
1853 l_int32 i, ret, format;
1854 TIFF *tif;
1855 
1856  PROCNAME("freadHeaderTiff");
1857 
1858  if (pw) *pw = 0;
1859  if (ph) *ph = 0;
1860  if (pbps) *pbps = 0;
1861  if (pspp) *pspp = 0;
1862  if (pres) *pres = 0;
1863  if (pcmap) *pcmap = 0;
1864  if (pformat) *pformat = 0;
1865  if (!fp)
1866  return ERROR_INT("stream not defined", procName, 1);
1867  if (n < 0)
1868  return ERROR_INT("image index must be >= 0", procName, 1);
1869  if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat)
1870  return ERROR_INT("no results requested", procName, 1);
1871 
1872  findFileFormatStream(fp, &format);
1873  if (!L_FORMAT_IS_TIFF(format))
1874  return ERROR_INT("file not tiff format", procName, 1);
1875 
1876  if ((tif = fopenTiff(fp, "r")) == NULL)
1877  return ERROR_INT("tif not open for read", procName, 1);
1878 
1879  for (i = 0; i < n; i++) {
1880  if (TIFFReadDirectory(tif) == 0)
1881  return ERROR_INT("image n not found in file", procName, 1);
1882  }
1883 
1884  ret = tiffReadHeaderTiff(tif, pw, ph, pbps, pspp, pres, pcmap, pformat);
1885  TIFFCleanup(tif);
1886  return ret;
1887 }
1888 
1889 
1910 l_ok
1911 readHeaderMemTiff(const l_uint8 *cdata,
1912  size_t size,
1913  l_int32 n,
1914  l_int32 *pw,
1915  l_int32 *ph,
1916  l_int32 *pbps,
1917  l_int32 *pspp,
1918  l_int32 *pres,
1919  l_int32 *pcmap,
1920  l_int32 *pformat)
1921 {
1922 l_uint8 *data;
1923 l_int32 i, ret;
1924 TIFF *tif;
1925 
1926  PROCNAME("readHeaderMemTiff");
1927 
1928  if (pw) *pw = 0;
1929  if (ph) *ph = 0;
1930  if (pbps) *pbps = 0;
1931  if (pspp) *pspp = 0;
1932  if (pres) *pres = 0;
1933  if (pcmap) *pcmap = 0;
1934  if (pformat) *pformat = 0;
1935  if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat)
1936  return ERROR_INT("no results requested", procName, 1);
1937  if (!cdata)
1938  return ERROR_INT("cdata not defined", procName, 1);
1939 
1940  /* Open a tiff stream to memory */
1941  data = (l_uint8 *)cdata; /* we're really not going to change this */
1942  if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL)
1943  return ERROR_INT("tiff stream not opened", procName, 1);
1944 
1945  for (i = 0; i < n; i++) {
1946  if (TIFFReadDirectory(tif) == 0) {
1947  TIFFClose(tif);
1948  return ERROR_INT("image n not found in file", procName, 1);
1949  }
1950  }
1951 
1952  ret = tiffReadHeaderTiff(tif, pw, ph, pbps, pspp, pres, pcmap, pformat);
1953  TIFFClose(tif);
1954  return ret;
1955 }
1956 
1957 
1971 static l_int32
1973  l_int32 *pw,
1974  l_int32 *ph,
1975  l_int32 *pbps,
1976  l_int32 *pspp,
1977  l_int32 *pres,
1978  l_int32 *pcmap,
1979  l_int32 *pformat)
1980 {
1981 l_uint16 tiffcomp;
1982 l_uint16 bps, spp;
1983 l_uint16 *rmap, *gmap, *bmap;
1984 l_int32 xres, yres;
1985 l_uint32 w, h;
1986 
1987  PROCNAME("tiffReadHeaderTiff");
1988 
1989  if (!tif)
1990  return ERROR_INT("tif not opened", procName, 1);
1991 
1992  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
1993  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
1994  TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
1995  TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
1996  if (w < 1 || h < 1)
1997  return ERROR_INT("tif w and h not both > 0", procName, 1);
1998  if (bps != 1 && bps != 2 && bps != 4 && bps != 8 && bps != 16)
1999  return ERROR_INT("bps not in set {1,2,4,8,16}", procName, 1);
2000  if (spp != 1 && spp != 2 && spp != 3 && spp != 4)
2001  return ERROR_INT("spp not in set {1,2,3,4}", procName, 1);
2002  if (pw) *pw = w;
2003  if (ph) *ph = h;
2004  if (pbps) *pbps = bps;
2005  if (pspp) *pspp = spp;
2006  if (pres) {
2007  *pres = 300; /* default ppi */
2008  if (getTiffStreamResolution(tif, &xres, &yres) == 0)
2009  *pres = (l_int32)xres;
2010  }
2011  if (pcmap) {
2012  *pcmap = 0;
2013  if (TIFFGetField(tif, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap))
2014  *pcmap = 1;
2015  }
2016  if (pformat) {
2017  TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp);
2018  *pformat = getTiffCompressedFormat(tiffcomp);
2019  }
2020  return 0;
2021 }
2022 
2023 
2043 l_ok
2045  l_int32 *pcomptype)
2046 {
2047 l_uint16 tiffcomp;
2048 TIFF *tif;
2049 
2050  PROCNAME("findTiffCompression");
2051 
2052  if (!pcomptype)
2053  return ERROR_INT("&comptype not defined", procName, 1);
2054  *pcomptype = IFF_UNKNOWN; /* init */
2055  if (!fp)
2056  return ERROR_INT("stream not defined", procName, 1);
2057 
2058  if ((tif = fopenTiff(fp, "r")) == NULL)
2059  return ERROR_INT("tif not opened", procName, 1);
2060  TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp);
2061  *pcomptype = getTiffCompressedFormat(tiffcomp);
2062  TIFFCleanup(tif);
2063  return 0;
2064 }
2065 
2066 
2081 static l_int32
2082 getTiffCompressedFormat(l_uint16 tiffcomp)
2083 {
2084 l_int32 comptype;
2085 
2086  switch (tiffcomp)
2087  {
2088  case COMPRESSION_CCITTFAX4:
2089  comptype = IFF_TIFF_G4;
2090  break;
2091  case COMPRESSION_CCITTFAX3:
2092  comptype = IFF_TIFF_G3;
2093  break;
2094  case COMPRESSION_CCITTRLE:
2095  comptype = IFF_TIFF_RLE;
2096  break;
2097  case COMPRESSION_PACKBITS:
2098  comptype = IFF_TIFF_PACKBITS;
2099  break;
2100  case COMPRESSION_LZW:
2101  comptype = IFF_TIFF_LZW;
2102  break;
2103  case COMPRESSION_ADOBE_DEFLATE:
2104  comptype = IFF_TIFF_ZIP;
2105  break;
2106  case COMPRESSION_JPEG:
2107  comptype = IFF_TIFF_JPEG;
2108  break;
2109  default:
2110  comptype = IFF_TIFF;
2111  break;
2112  }
2113  return comptype;
2114 }
2115 
2116 
2117 /*--------------------------------------------------------------*
2118  * Extraction of tiff g4 data *
2119  *--------------------------------------------------------------*/
2131 l_ok
2132 extractG4DataFromFile(const char *filein,
2133  l_uint8 **pdata,
2134  size_t *pnbytes,
2135  l_int32 *pw,
2136  l_int32 *ph,
2137  l_int32 *pminisblack)
2138 {
2139 l_uint8 *inarray, *data;
2140 l_uint16 minisblack, comptype; /* accessors require l_uint16 */
2141 l_int32 istiff;
2142 l_uint32 w, h, rowsperstrip; /* accessors require l_uint32 */
2143 l_uint32 diroff;
2144 size_t fbytes, nbytes;
2145 FILE *fpin;
2146 TIFF *tif;
2147 
2148  PROCNAME("extractG4DataFromFile");
2149 
2150  if (!pdata)
2151  return ERROR_INT("&data not defined", procName, 1);
2152  if (!pnbytes)
2153  return ERROR_INT("&nbytes not defined", procName, 1);
2154  if (!pw && !ph && !pminisblack)
2155  return ERROR_INT("no output data requested", procName, 1);
2156  *pdata = NULL;
2157  *pnbytes = 0;
2158 
2159  if ((fpin = fopenReadStream(filein)) == NULL)
2160  return ERROR_INT("stream not opened to file", procName, 1);
2161  istiff = fileFormatIsTiff(fpin);
2162  fclose(fpin);
2163  if (!istiff)
2164  return ERROR_INT("filein not tiff", procName, 1);
2165 
2166  if ((inarray = l_binaryRead(filein, &fbytes)) == NULL)
2167  return ERROR_INT("inarray not made", procName, 1);
2168 
2169  /* Get metadata about the image */
2170  if ((tif = openTiff(filein, "rb")) == NULL) {
2171  LEPT_FREE(inarray);
2172  return ERROR_INT("tif not open for read", procName, 1);
2173  }
2174  TIFFGetField(tif, TIFFTAG_COMPRESSION, &comptype);
2175  if (comptype != COMPRESSION_CCITTFAX4) {
2176  LEPT_FREE(inarray);
2177  TIFFClose(tif);
2178  return ERROR_INT("filein is not g4 compressed", procName, 1);
2179  }
2180 
2181  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
2182  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
2183  TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
2184  if (h != rowsperstrip)
2185  L_WARNING("more than 1 strip\n", procName);
2186  TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &minisblack); /* for 1 bpp */
2187 /* TIFFPrintDirectory(tif, stderr, 0); */
2188  TIFFClose(tif);
2189  if (pw) *pw = (l_int32)w;
2190  if (ph) *ph = (l_int32)h;
2191  if (pminisblack) *pminisblack = (l_int32)minisblack;
2192 
2193  /* The header has 8 bytes: the first 2 are the magic number,
2194  * the next 2 are the version, and the last 4 are the
2195  * offset to the first directory. That's what we want here.
2196  * We have to test the byte order before decoding 4 bytes! */
2197  if (inarray[0] == 0x4d) { /* big-endian */
2198  diroff = (inarray[4] << 24) | (inarray[5] << 16) |
2199  (inarray[6] << 8) | inarray[7];
2200  } else { /* inarray[0] == 0x49 : little-endian */
2201  diroff = (inarray[7] << 24) | (inarray[6] << 16) |
2202  (inarray[5] << 8) | inarray[4];
2203  }
2204 /* lept_stderr(" diroff = %d, %x\n", diroff, diroff); */
2205 
2206  /* Extract the ccittg4 encoded data from the tiff file.
2207  * We skip the 8 byte header and take nbytes of data,
2208  * up to the beginning of the directory (at diroff) */
2209  nbytes = diroff - 8;
2210  if (nbytes > MaxNumTiffBytes) {
2211  LEPT_FREE(inarray);
2212  L_ERROR("requesting %zu bytes > %zu\n", procName,
2213  nbytes, MaxNumTiffBytes);
2214  return 1;
2215  }
2216  *pnbytes = nbytes;
2217  if ((data = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) {
2218  LEPT_FREE(inarray);
2219  return ERROR_INT("data not allocated", procName, 1);
2220  }
2221  *pdata = data;
2222  memcpy(data, inarray + 8, nbytes);
2223  LEPT_FREE(inarray);
2224 
2225  return 0;
2226 }
2227 
2228 
2229 /*--------------------------------------------------------------*
2230  * Open tiff stream from file stream *
2231  *--------------------------------------------------------------*/
2252 static TIFF *
2253 fopenTiff(FILE *fp,
2254  const char *modestring)
2255 {
2256  PROCNAME("fopenTiff");
2257 
2258  if (!fp)
2259  return (TIFF *)ERROR_PTR("stream not opened", procName, NULL);
2260  if (!modestring)
2261  return (TIFF *)ERROR_PTR("modestring not defined", procName, NULL);
2262 
2263  TIFFSetWarningHandler(NULL); /* disable warnings */
2264  TIFFSetErrorHandler(NULL); /* disable error messages */
2265 
2266  fseek(fp, 0, SEEK_SET);
2267  return TIFFClientOpen("TIFFstream", modestring, (thandle_t)fp,
2268  lept_read_proc, lept_write_proc, lept_seek_proc,
2269  lept_close_proc, lept_size_proc, NULL, NULL);
2270 }
2271 
2272 
2273 /*--------------------------------------------------------------*
2274  * Wrapper for TIFFOpen *
2275  *--------------------------------------------------------------*/
2288 static TIFF *
2289 openTiff(const char *filename,
2290  const char *modestring)
2291 {
2292 char *fname;
2293 TIFF *tif;
2294 
2295  PROCNAME("openTiff");
2296 
2297  if (!filename)
2298  return (TIFF *)ERROR_PTR("filename not defined", procName, NULL);
2299  if (!modestring)
2300  return (TIFF *)ERROR_PTR("modestring not defined", procName, NULL);
2301 
2302  TIFFSetWarningHandler(NULL); /* disable warnings */
2303  TIFFSetErrorHandler(NULL); /* disable error messages */
2304 
2305  fname = genPathname(filename, NULL);
2306  tif = TIFFOpen(fname, modestring);
2307  LEPT_FREE(fname);
2308  return tif;
2309 }
2310 
2311 
2312 /*----------------------------------------------------------------------*
2313  * Memory I/O: reading memory --> pix and writing pix --> memory *
2314  *----------------------------------------------------------------------*/
2315 /* It would be nice to use open_memstream() and fmemopen()
2316  * for writing and reading to memory, rsp. These functions manage
2317  * memory for writes and reads that use a file streams interface.
2318  * Unfortunately, the tiff library only has an interface for reading
2319  * and writing to file descriptors, not to file streams. The tiff
2320  * library procedure is to open a "tiff stream" and read/write to it.
2321  * The library provides a client interface for managing the I/O
2322  * from memory, which requires seven callbacks. See the TIFFClientOpen
2323  * man page for callback signatures. Adam Langley provided the code
2324  * to do this. */
2325 
2347 {
2348  l_uint8 *buffer; /* expands to hold data when written to; */
2349  /* fixed size when read from. */
2350  size_t bufsize; /* current size allocated when written to; */
2351  /* fixed size of input data when read from. */
2352  size_t offset; /* byte offset from beginning of buffer. */
2353  size_t hw; /* high-water mark; max bytes in buffer. */
2354  l_uint8 **poutdata; /* input param for writing; data goes here. */
2355  size_t *poutsize; /* input param for writing; data size goes here. */
2356 };
2357 typedef struct L_Memstream L_MEMSTREAM;
2358 
2359 
2360  /* These are static functions for memory I/O */
2361 static L_MEMSTREAM *memstreamCreateForRead(l_uint8 *indata, size_t pinsize);
2362 static L_MEMSTREAM *memstreamCreateForWrite(l_uint8 **poutdata,
2363  size_t *poutsize);
2364 static tsize_t tiffReadCallback(thandle_t handle, tdata_t data, tsize_t length);
2365 static tsize_t tiffWriteCallback(thandle_t handle, tdata_t data,
2366  tsize_t length);
2367 static toff_t tiffSeekCallback(thandle_t handle, toff_t offset, l_int32 whence);
2368 static l_int32 tiffCloseCallback(thandle_t handle);
2369 static toff_t tiffSizeCallback(thandle_t handle);
2370 static l_int32 tiffMapCallback(thandle_t handle, tdata_t *data, toff_t *length);
2371 static void tiffUnmapCallback(thandle_t handle, tdata_t data, toff_t length);
2372 
2373 
2374 static L_MEMSTREAM *
2375 memstreamCreateForRead(l_uint8 *indata,
2376  size_t insize)
2377 {
2378 L_MEMSTREAM *mstream;
2379 
2380  mstream = (L_MEMSTREAM *)LEPT_CALLOC(1, sizeof(L_MEMSTREAM));
2381  mstream->buffer = indata; /* handle to input data array */
2382  mstream->bufsize = insize; /* amount of input data */
2383  mstream->hw = insize; /* high-water mark fixed at input data size */
2384  mstream->offset = 0; /* offset always starts at 0 */
2385  return mstream;
2386 }
2387 
2388 
2389 static L_MEMSTREAM *
2390 memstreamCreateForWrite(l_uint8 **poutdata,
2391  size_t *poutsize)
2392 {
2393 L_MEMSTREAM *mstream;
2394 
2395  mstream = (L_MEMSTREAM *)LEPT_CALLOC(1, sizeof(L_MEMSTREAM));
2396  mstream->buffer = (l_uint8 *)LEPT_CALLOC(8 * 1024, 1);
2397  mstream->bufsize = 8 * 1024;
2398  mstream->poutdata = poutdata; /* used only at end of write */
2399  mstream->poutsize = poutsize; /* ditto */
2400  mstream->hw = mstream->offset = 0;
2401  return mstream;
2402 }
2403 
2404 
2405 static tsize_t
2406 tiffReadCallback(thandle_t handle,
2407  tdata_t data,
2408  tsize_t length)
2409 {
2410 L_MEMSTREAM *mstream;
2411 size_t amount;
2412 
2413  mstream = (L_MEMSTREAM *)handle;
2414  amount = L_MIN((size_t)length, mstream->hw - mstream->offset);
2415 
2416  /* Fuzzed files can create this condition! */
2417  if (mstream->offset + amount < amount || /* overflow */
2418  mstream->offset + amount > mstream->hw) {
2419  lept_stderr("Bad file: amount too big: %zu\n", amount);
2420  return 0;
2421  }
2422 
2423  memcpy(data, mstream->buffer + mstream->offset, amount);
2424  mstream->offset += amount;
2425  return amount;
2426 }
2427 
2428 
2429 static tsize_t
2430 tiffWriteCallback(thandle_t handle,
2431  tdata_t data,
2432  tsize_t length)
2433 {
2434 L_MEMSTREAM *mstream;
2435 size_t newsize;
2436 
2437  /* reallocNew() uses calloc to initialize the array.
2438  * If malloc is used instead, for some of the encoding methods,
2439  * not all the data in 'bufsize' bytes in the buffer will
2440  * have been initialized by the end of the compression. */
2441  mstream = (L_MEMSTREAM *)handle;
2442  if (mstream->offset + length > mstream->bufsize) {
2443  newsize = 2 * (mstream->offset + length);
2444  mstream->buffer = (l_uint8 *)reallocNew((void **)&mstream->buffer,
2445  mstream->hw, newsize);
2446  mstream->bufsize = newsize;
2447  }
2448 
2449  memcpy(mstream->buffer + mstream->offset, data, length);
2450  mstream->offset += length;
2451  mstream->hw = L_MAX(mstream->offset, mstream->hw);
2452  return length;
2453 }
2454 
2455 
2456 static toff_t
2457 tiffSeekCallback(thandle_t handle,
2458  toff_t offset,
2459  l_int32 whence)
2460 {
2461 L_MEMSTREAM *mstream;
2462 
2463  PROCNAME("tiffSeekCallback");
2464  mstream = (L_MEMSTREAM *)handle;
2465  switch (whence) {
2466  case SEEK_SET:
2467 /* lept_stderr("seek_set: offset = %d\n", offset); */
2468  if((size_t)offset != offset) { /* size_t overflow on uint32 */
2469  return (toff_t)ERROR_INT("too large offset value", procName, 1);
2470  }
2471  mstream->offset = offset;
2472  break;
2473  case SEEK_CUR:
2474 /* lept_stderr("seek_cur: offset = %d\n", offset); */
2475  mstream->offset += offset;
2476  break;
2477  case SEEK_END:
2478 /* lept_stderr("seek end: hw = %d, offset = %d\n",
2479  mstream->hw, offset); */
2480  mstream->offset = mstream->hw - offset; /* offset >= 0 */
2481  break;
2482  default:
2483  return (toff_t)ERROR_INT("bad whence value", procName,
2484  mstream->offset);
2485  }
2486 
2487  return mstream->offset;
2488 }
2489 
2490 
2491 static l_int32
2492 tiffCloseCallback(thandle_t handle)
2493 {
2494 L_MEMSTREAM *mstream;
2495 
2496  mstream = (L_MEMSTREAM *)handle;
2497  if (mstream->poutdata) { /* writing: save the output data */
2498  *mstream->poutdata = mstream->buffer;
2499  *mstream->poutsize = mstream->hw;
2500  }
2501  LEPT_FREE(mstream); /* never free the buffer! */
2502  return 0;
2503 }
2504 
2505 
2506 static toff_t
2507 tiffSizeCallback(thandle_t handle)
2508 {
2509 L_MEMSTREAM *mstream;
2510 
2511  mstream = (L_MEMSTREAM *)handle;
2512  return mstream->hw;
2513 }
2514 
2515 
2516 static l_int32
2517 tiffMapCallback(thandle_t handle,
2518  tdata_t *data,
2519  toff_t *length)
2520 {
2521 L_MEMSTREAM *mstream;
2522 
2523  mstream = (L_MEMSTREAM *)handle;
2524  *data = mstream->buffer;
2525  *length = mstream->hw;
2526  return 0;
2527 }
2528 
2529 
2530 static void
2531 tiffUnmapCallback(thandle_t handle,
2532  tdata_t data,
2533  toff_t length)
2534 {
2535  return;
2536 }
2537 
2538 
2559 static TIFF *
2560 fopenTiffMemstream(const char *filename,
2561  const char *operation,
2562  l_uint8 **pdata,
2563  size_t *pdatasize)
2564 {
2565 L_MEMSTREAM *mstream;
2566 TIFF *tif;
2567 
2568  PROCNAME("fopenTiffMemstream");
2569 
2570  if (!filename)
2571  return (TIFF *)ERROR_PTR("filename not defined", procName, NULL);
2572  if (!operation)
2573  return (TIFF *)ERROR_PTR("operation not defined", procName, NULL);
2574  if (!pdata)
2575  return (TIFF *)ERROR_PTR("&data not defined", procName, NULL);
2576  if (!pdatasize)
2577  return (TIFF *)ERROR_PTR("&datasize not defined", procName, NULL);
2578  if (strcmp(operation, "r") && strcmp(operation, "w"))
2579  return (TIFF *)ERROR_PTR("op not 'r' or 'w'", procName, NULL);
2580 
2581  if (!strcmp(operation, "r"))
2582  mstream = memstreamCreateForRead(*pdata, *pdatasize);
2583  else
2584  mstream = memstreamCreateForWrite(pdata, pdatasize);
2585 
2586  TIFFSetWarningHandler(NULL); /* disable warnings */
2587  TIFFSetErrorHandler(NULL); /* disable error messages */
2588 
2589  tif = TIFFClientOpen(filename, operation, (thandle_t)mstream,
2590  tiffReadCallback, tiffWriteCallback,
2591  tiffSeekCallback, tiffCloseCallback,
2592  tiffSizeCallback, tiffMapCallback,
2593  tiffUnmapCallback);
2594  if (!tif)
2595  LEPT_FREE(mstream);
2596  return tif;
2597 }
2598 
2599 
2620 PIX *
2621 pixReadMemTiff(const l_uint8 *cdata,
2622  size_t size,
2623  l_int32 n)
2624 {
2625 l_uint8 *data;
2626 l_int32 i;
2627 PIX *pix;
2628 TIFF *tif;
2629 
2630  PROCNAME("pixReadMemTiff");
2631 
2632  if (!cdata)
2633  return (PIX *)ERROR_PTR("cdata not defined", procName, NULL);
2634 
2635  data = (l_uint8 *)cdata; /* we're really not going to change this */
2636  if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL)
2637  return (PIX *)ERROR_PTR("tiff stream not opened", procName, NULL);
2638 
2639  pix = NULL;
2640  for (i = 0; ; i++) {
2641  if (i == n) {
2642  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
2643  TIFFClose(tif);
2644  return NULL;
2645  }
2646  pixSetInputFormat(pix, IFF_TIFF);
2647  break;
2648  }
2649  if (TIFFReadDirectory(tif) == 0)
2650  break;
2651  if (i == ManyPagesInTiffFile + 1) {
2652  L_WARNING("big file: more than %d pages\n", procName,
2653  ManyPagesInTiffFile);
2654  }
2655  }
2656 
2657  TIFFClose(tif);
2658  return pix;
2659 }
2660 
2661 
2685 PIX *
2686 pixReadMemFromMultipageTiff(const l_uint8 *cdata,
2687  size_t size,
2688  size_t *poffset)
2689 {
2690 l_uint8 *data;
2691 l_int32 retval;
2692 size_t offset;
2693 PIX *pix;
2694 TIFF *tif;
2695 
2696  PROCNAME("pixReadMemFromMultipageTiff");
2697 
2698  if (!cdata)
2699  return (PIX *)ERROR_PTR("cdata not defined", procName, NULL);
2700  if (!poffset)
2701  return (PIX *)ERROR_PTR("&offset not defined", procName, NULL);
2702 
2703  data = (l_uint8 *)cdata; /* we're really not going to change this */
2704  if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL)
2705  return (PIX *)ERROR_PTR("tiff stream not opened", procName, NULL);
2706 
2707  /* Set ptrs in the TIFF to the beginning of the image */
2708  offset = *poffset;
2709  retval = (offset == 0) ? TIFFSetDirectory(tif, 0)
2710  : TIFFSetSubDirectory(tif, offset);
2711  if (retval == 0) {
2712  TIFFClose(tif);
2713  return NULL;
2714  }
2715 
2716  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
2717  TIFFClose(tif);
2718  return NULL;
2719  }
2720 
2721  /* Advance to the next image and return the new offset */
2722  TIFFReadDirectory(tif);
2723  *poffset = TIFFCurrentDirOffset(tif);
2724  TIFFClose(tif);
2725  return pix;
2726 }
2727 
2728 
2741 PIXA *
2742 pixaReadMemMultipageTiff(const l_uint8 *data,
2743  size_t size)
2744 {
2745 size_t offset;
2746 PIX *pix;
2747 PIXA *pixa;
2748 
2749  PROCNAME("pixaReadMemMultipageTiff");
2750 
2751  if (!data)
2752  return (PIXA *)ERROR_PTR("data not defined", procName, NULL);
2753 
2754  offset = 0;
2755  pixa = pixaCreate(0);
2756  do {
2757  pix = pixReadMemFromMultipageTiff(data, size, &offset);
2758  pixaAddPix(pixa, pix, L_INSERT);
2759  } while (offset != 0);
2760  return pixa;
2761 }
2762 
2763 
2781 l_ok
2783  size_t *psize,
2784  PIXA *pixa)
2785 {
2786 const char *modestr;
2787 l_int32 i, n;
2788 FILE *fp;
2789 PIX *pix1;
2790 
2791  PROCNAME("pixaWriteMemMultipageTiff");
2792 
2793  if (pdata) *pdata = NULL;
2794  if (!pdata)
2795  return ERROR_INT("pdata not defined", procName, 1);
2796  if (!pixa)
2797  return ERROR_INT("pixa not defined", procName, 1);
2798 
2799 #ifdef _WIN32
2800  if ((fp = fopenWriteWinTempfile()) == NULL)
2801  return ERROR_INT("tmpfile stream not opened", procName, 1);
2802 #else
2803  if ((fp = tmpfile()) == NULL)
2804  return ERROR_INT("tmpfile stream not opened", procName, 1);
2805 #endif /* _WIN32 */
2806 
2807  n = pixaGetCount(pixa);
2808  for (i = 0; i < n; i++) {
2809  modestr = (i == 0) ? "w" : "a";
2810  pix1 = pixaGetPix(pixa, i, L_CLONE);
2811  if (pixGetDepth(pix1) == 1)
2812  pixWriteStreamTiffWA(fp, pix1, IFF_TIFF_G4, modestr);
2813  else
2814  pixWriteStreamTiffWA(fp, pix1, IFF_TIFF_ZIP, modestr);
2815  pixDestroy(&pix1);
2816  }
2817 
2818  rewind(fp);
2819  *pdata = l_binaryReadStream(fp, psize);
2820  fclose(fp);
2821  return 0;
2822 }
2823 
2824 
2840 l_ok
2841 pixWriteMemTiff(l_uint8 **pdata,
2842  size_t *psize,
2843  PIX *pix,
2844  l_int32 comptype)
2845 {
2846  return pixWriteMemTiffCustom(pdata, psize, pix, comptype,
2847  NULL, NULL, NULL, NULL);
2848 }
2849 
2850 
2871 l_ok
2872 pixWriteMemTiffCustom(l_uint8 **pdata,
2873  size_t *psize,
2874  PIX *pix,
2875  l_int32 comptype,
2876  NUMA *natags,
2877  SARRAY *savals,
2878  SARRAY *satypes,
2879  NUMA *nasizes)
2880 {
2881 l_int32 ret;
2882 TIFF *tif;
2883 
2884  PROCNAME("pixWriteMemTiffCustom");
2885 
2886  if (!pdata)
2887  return ERROR_INT("&data not defined", procName, 1);
2888  if (!psize)
2889  return ERROR_INT("&size not defined", procName, 1);
2890  if (!pix)
2891  return ERROR_INT("&pix not defined", procName, 1);
2892  if (pixGetDepth(pix) != 1 && comptype != IFF_TIFF &&
2893  comptype != IFF_TIFF_LZW && comptype != IFF_TIFF_ZIP &&
2894  comptype != IFF_TIFF_JPEG) {
2895  L_WARNING("invalid compression type for bpp > 1\n", procName);
2896  comptype = IFF_TIFF_ZIP;
2897  }
2898 
2899  if ((tif = fopenTiffMemstream("tifferror", "w", pdata, psize)) == NULL)
2900  return ERROR_INT("tiff stream not opened", procName, 1);
2901  ret = pixWriteToTiffStream(tif, pix, comptype, natags, savals,
2902  satypes, nasizes);
2903 
2904  TIFFClose(tif);
2905  return ret;
2906 }
2907 
2908 /* --------------------------------------------*/
2909 #endif /* HAVE_LIBTIFF */
2910 /* --------------------------------------------*/
l_ok pixWriteTiff(const char *filename, PIX *pix, l_int32 comptype, const char *modestr)
pixWriteTiff()
Definition: tiffio.c:795
PIX * pixFlipLR(PIX *pixd, PIX *pixs)
pixFlipLR()
Definition: rotateorth.c:427
l_ok readHeaderTiff(const char *filename, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat)
readHeaderTiff()
Definition: tiffio.c:1787
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
PIX * pixReadStreamTiff(FILE *fp, l_int32 n)
pixReadStreamTiff()
Definition: tiffio.c:420
static l_int32 getTiffStreamResolution(TIFF *tif, l_int32 *pxres, l_int32 *pyres)
getTiffStreamResolution()
Definition: tiffio.c:1721
l_ok pixWriteMemTiff(l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 comptype)
pixWriteMemTiff()
Definition: tiffio.c:2841
l_ok fprintTiffInfo(FILE *fpout, const char *tiffile)
fprintTiffInfo()
Definition: tiffio.c:1604
PIXA * pixaReadMultipageTiff(const char *filename)
pixaReadMultipageTiff()
Definition: tiffio.c:1401
l_ok pixWriteStreamTiffWA(FILE *fp, PIX *pix, l_int32 comptype, const char *modestr)
pixWriteStreamTiffWA()
Definition: tiffio.c:936
l_ok extractG4DataFromFile(const char *filein, l_uint8 **pdata, size_t *pnbytes, l_int32 *pw, l_int32 *ph, l_int32 *pminisblack)
extractG4DataFromFile()
Definition: tiffio.c:2132
static l_int32 tiffReadHeaderTiff(TIFF *tif, l_int32 *pwidth, l_int32 *pheight, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat)
tiffReadHeaderTiff()
Definition: tiffio.c:1972
Definition: pix.h:204
char * genPathname(const char *dir, const char *fname)
genPathname()
Definition: utils2.c:3173
Definition: pix.h:713
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_ok pixaWriteMultipageTiff(const char *fname, PIXA *pixa)
pixaWriteMultipageTiff()
Definition: tiffio.c:1461
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
Definition: pix.h:710
l_ok writeMultipageTiff(const char *dirin, const char *substr, const char *fileout)
writeMultipageTiff()
Definition: tiffio.c:1515
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
l_ok pixWriteStreamTiff(FILE *fp, PIX *pix, l_int32 comptype)
pixWriteStreamTiff()
Definition: tiffio.c:911
l_ok readHeaderMemTiff(const l_uint8 *cdata, size_t size, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat)
readHeaderMemTiff()
Definition: tiffio.c:1911
Memory stream buffer used with TIFFClientOpen()
Definition: tiffio.c:2346
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
Definition: array.h:126
PIX * pixReadMemFromMultipageTiff(const l_uint8 *cdata, size_t size, size_t *poffset)
pixReadMemFromMultipageTiff()
Definition: tiffio.c:2686
l_ok pixSetText(PIX *pix, const char *textstring)
pixSetText()
Definition: pix1.c:1536
static TIFF * fopenTiff(FILE *fp, const char *modestring)
fopenTiff()
Definition: tiffio.c:2253
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
l_ok freadHeaderTiff(FILE *fp, l_int32 n, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *pres, l_int32 *pcmap, l_int32 *pformat)
freadHeaderTiff()
Definition: tiffio.c:1843
l_ok findFileFormatStream(FILE *fp, l_int32 *pformat)
findFileFormatStream()
Definition: readfile.c:619
l_uint8 * l_binaryRead(const char *filename, size_t *pnbytes)
l_binaryRead()
Definition: utils2.c:1352
PIX * pixEndianTwoByteSwapNew(PIX *pixs)
pixEndianTwoByteSwapNew()
Definition: pix2.c:3185
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
PIX * pixFlipTB(PIX *pixd, PIX *pixs)
pixFlipTB()
Definition: rotateorth.c:605
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
Definition: array.h:70
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
l_ok getTiffResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres)
getTiffResolution()
Definition: tiffio.c:1685
l_ok pixWriteTiffCustom(const char *filename, PIX *pix, l_int32 comptype, const char *modestr, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes)
pixWriteTiffCustom()
Definition: tiffio.c:852
static TIFF * fopenTiffMemstream(const char *filename, const char *operation, l_uint8 **pdata, size_t *pdatasize)
fopenTiffMemstream()
Definition: tiffio.c:2560
l_ok pixEndianByteSwap(PIX *pixs)
pixEndianByteSwap()
Definition: pix2.c:3074
l_ok composeRGBAPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_int32 aval, l_uint32 *ppixel)
composeRGBAPixel()
Definition: pix2.c:2783
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
l_ok pixSetPadBits(PIX *pix, l_int32 val)
pixSetPadBits()
Definition: pix2.c:1382
static PIX * pixReadFromTiffStream(TIFF *tif)
pixReadFromTiffStream()
Definition: tiffio.c:485
l_ok pixEndianTwoByteSwap(PIX *pixs)
pixEndianTwoByteSwap()
Definition: pix2.c:3239
static l_int32 pixWriteToTiffStream(TIFF *tif, PIX *pix, l_int32 comptype, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes)
pixWriteToTiffStream()
Definition: tiffio.c:1011
static TIFF * openTiff(const char *filename, const char *modestring)
openTiff()
Definition: tiffio.c:2289
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
l_ok findFileFormat(const char *filename, l_int32 *pformat)
findFileFormat()
Definition: readfile.c:584
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:703
void * reallocNew(void **pindata, size_t oldsize, size_t newsize)
reallocNew()
Definition: utils2.c:1302
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
Definition: pix.h:711
SARRAY * getSortedPathnamesInDirectory(const char *dirname, const char *substr, l_int32 first, l_int32 nfiles)
getSortedPathnamesInDirectory()
Definition: sarray1.c:1848
Definition: pix.h:455
PIX * pixReadMemTiff(const l_uint8 *cdata, size_t size, l_int32 n)
pixReadMemTiff()
Definition: tiffio.c:2621
PIX * pixEndianByteSwapNew(PIX *pixs)
pixEndianByteSwapNew()
Definition: pix2.c:3010
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
FILE * fopenWriteWinTempfile(void)
fopenWriteWinTempfile()
Definition: utils2.c:2055
l_int32 fileFormatIsTiff(FILE *fp)
fileFormatIsTiff()
Definition: readfile.c:800
FILE * fopenReadStream(const char *filename)
fopenReadStream()
Definition: utils2.c:1932
l_uint8 * l_binaryReadStream(FILE *fp, size_t *pnbytes)
l_binaryReadStream()
Definition: utils2.c:1402
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:643
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:193
l_ok writeMultipageTiffSA(SARRAY *sa, const char *fileout)
writeMultipageTiffSA()
Definition: tiffio.c:1551
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1512
l_ok findTiffCompression(FILE *fp, l_int32 *pcomptype)
findTiffCompression()
Definition: tiffio.c:2044
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:691
static l_int32 getTiffCompressedFormat(l_uint16 tiffcomp)
getTiffCompressedFormat()
Definition: tiffio.c:2082
l_ok pixaWriteMemMultipageTiff(l_uint8 **pdata, size_t *psize, PIXA *pixa)
pixaWriteMemMultipageTiff()
Definition: tiffio.c:2782
Definition: pix.h:138
PIX * pixReadTiff(const char *filename, l_int32 n)
pixReadTiff()
Definition: tiffio.c:383
PIX * pixReadFromMultipageTiff(const char *fname, size_t *poffset)
pixReadFromMultipageTiff()
Definition: tiffio.c:1352
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
PIX * pixRotate90(PIX *pixs, l_int32 direction)
pixRotate90()
Definition: rotateorth.c:166
static l_int32 writeCustomTiffTags(TIFF *tif, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes)
writeCustomTiffTags()
Definition: tiffio.c:1224
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2751
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
l_ok tiffGetCount(FILE *fp, l_int32 *pn)
tiffGetCount()
Definition: tiffio.c:1637
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:650
l_ok pixWriteMemTiffCustom(l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 comptype, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes)
pixWriteMemTiffCustom()
Definition: tiffio.c:2872
l_ok pixcmapToArrays(const PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap)
pixcmapToArrays()
Definition: colormap.c:2068
PIXA * pixaReadMemMultipageTiff(const l_uint8 *data, size_t size)
pixaReadMemMultipageTiff()
Definition: tiffio.c:2742
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:362