Leptonica  1.83.1
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 
118 #ifdef HAVE_CONFIG_H
119 #include <config_auto.h>
120 #endif /* HAVE_CONFIG_H */
121 
122 #include <string.h>
123 #include <math.h> /* for isnan */
124 #include <sys/types.h>
125 #ifndef _MSC_VER
126 #include <unistd.h>
127 #else /* _MSC_VER */
128 #include <io.h>
129 #endif /* _MSC_VER */
130 #include <fcntl.h>
131 #include "allheaders.h"
132 
133 /* --------------------------------------------*/
134 #if HAVE_LIBTIFF /* defined in environ.h */
135 /* --------------------------------------------*/
136 
137 #include "tiff.h"
138 #include "tiffio.h"
139 
140 static const l_int32 DefaultResolution = 300; /* ppi */
141 static const l_int32 ManyPagesInTiffFile = 3000; /* warn if big */
142 
143  /* Verified that tiflib makes valid g4 files of this size */
144 static const l_int32 MaxTiffWidth = 1 << 20; /* 1M pixels */
145 static const l_int32 MaxTiffHeight = 1 << 20; /* 1M pixels */
146 
147  /* Check g4 data size */
148 static const size_t MaxNumTiffBytes = (1 << 28) - 1; /* 256 MB */
149 
150  /* All functions with TIFF interfaces are static. */
151 static PIX *pixReadFromTiffStream(TIFF *tif);
152 static l_int32 getTiffStreamResolution(TIFF *tif, l_int32 *pxres,
153  l_int32 *pyres);
154 static l_int32 tiffReadHeaderTiff(TIFF *tif, l_int32 *pwidth,
155  l_int32 *pheight, l_int32 *pbps,
156  l_int32 *pspp, l_int32 *pres,
157  l_int32 *pcmap, l_int32 *pformat);
158 static l_int32 writeCustomTiffTags(TIFF *tif, NUMA *natags,
159  SARRAY *savals, SARRAY *satypes,
160  NUMA *nasizes);
161 static l_int32 pixWriteToTiffStream(TIFF *tif, PIX *pix, l_int32 comptype,
162  NUMA *natags, SARRAY *savals,
163  SARRAY *satypes, NUMA *nasizes);
164 static TIFF *fopenTiff(FILE *fp, const char *modestring);
165 static TIFF *openTiff(const char *filename, const char *modestring);
166 
167  /* Static helper for tiff compression type */
168 static l_int32 getTiffCompressedFormat(l_uint16 tiffcomp);
169 
170  /* Static function for memory I/O */
171 static TIFF *fopenTiffMemstream(const char *filename, const char *operation,
172  l_uint8 **pdata, size_t *pdatasize);
173 
174  /* This structure defines a transform to be performed on a TIFF image
175  * (note that the same transformation can be represented in
176  * several different ways using this structure since
177  * vflip + hflip + counterclockwise == clockwise). */
179  int vflip; /* if non-zero, image needs a vertical fip */
180  int hflip; /* if non-zero, image needs a horizontal flip */
181  int rotate; /* -1 -> counterclockwise 90-degree rotation,
182  0 -> no rotation
183  1 -> clockwise 90-degree rotation */
184 };
185 
186  /* This describes the transformations needed for a given orientation
187  * tag. The tag values start at 1, so you need to subtract 1 to get a
188  * valid index into this array. It is only valid when not using
189  * TIFFReadRGBAImageOriented(). */
190 static struct tiff_transform tiff_orientation_transforms[] = {
191  {0, 0, 0},
192  {0, 1, 0},
193  {1, 1, 0},
194  {1, 0, 0},
195  {0, 1, -1},
196  {0, 0, 1},
197  {0, 1, 1},
198  {0, 0, -1}
199 };
200 
201  /* Same as above, except that test transformations are only valid
202  * when using TIFFReadRGBAImageOriented(). Transformations
203  * were determined empirically. See the libtiff mailing list for
204  * more discussion: http://www.asmail.be/msg0054683875.html */
205 static struct tiff_transform tiff_partial_orientation_transforms[] = {
206  {0, 0, 0},
207  {0, 0, 0},
208  {0, 0, 0},
209  {0, 0, 0},
210  {0, 1, -1},
211  {0, 1, 1},
212  {1, 0, 1},
213  {0, 1, -1}
214 };
215 
216 
217 /*-----------------------------------------------------------------------*
218  * TIFFClientOpen() wrappers for FILE* *
219  * Provided by Jürgen Buchmüller *
220  * *
221  * We previously used TIFFFdOpen(), which used low-level file *
222  * descriptors. It had portability issues with Windows, along *
223  * with other limitations from lack of stream control operations. *
224  * These callbacks to TIFFClientOpen() avoid the problems. *
225  * *
226  * Jürgen made the functions use 64 bit file operations where possible *
227  * or required, namely for seek and size. On Windows there are specific *
228  * _fseeki64() and _ftelli64() functions. On unix it is common to look *
229  * for a macro _LARGEFILE64_SOURCE being defined, which makes available *
230  * the off64_t type, and to use fseeko() and ftello() in this case. *
231  *-----------------------------------------------------------------------*/
232 static tsize_t
233 lept_read_proc(thandle_t cookie,
234  tdata_t buff,
235  tsize_t size)
236 {
237  FILE* fp = (FILE *)cookie;
238  tsize_t done;
239  if (!buff || !cookie || !fp)
240  return (tsize_t)-1;
241  done = fread(buff, 1, size, fp);
242  return done;
243 }
244 
245 static tsize_t
246 lept_write_proc(thandle_t cookie,
247  tdata_t buff,
248  tsize_t size)
249 {
250  FILE* fp = (FILE *)cookie;
251  tsize_t done;
252  if (!buff || !cookie || !fp)
253  return (tsize_t)-1;
254  done = fwrite(buff, 1, size, fp);
255  return done;
256 }
257 
258 static toff_t
259 lept_seek_proc(thandle_t cookie,
260  toff_t offs,
261  int whence)
262 {
263  FILE* fp = (FILE *)cookie;
264 #if defined(_MSC_VER)
265  __int64 pos = 0;
266  if (!cookie || !fp)
267  return (tsize_t)-1;
268  switch (whence) {
269  case SEEK_SET:
270  pos = 0;
271  break;
272  case SEEK_CUR:
273  pos = ftell(fp);
274  break;
275  case SEEK_END:
276  _fseeki64(fp, 0, SEEK_END);
277  pos = _ftelli64(fp);
278  break;
279  }
280  pos = (__int64)(pos + offs);
281  _fseeki64(fp, pos, SEEK_SET);
282  if (pos == _ftelli64(fp))
283  return (tsize_t)pos;
284 #elif defined(_LARGEFILE64_SOURCE)
285  off64_t pos = 0;
286  if (!cookie || !fp)
287  return (tsize_t)-1;
288  switch (whence) {
289  case SEEK_SET:
290  pos = 0;
291  break;
292  case SEEK_CUR:
293  pos = ftello(fp);
294  break;
295  case SEEK_END:
296  fseeko(fp, 0, SEEK_END);
297  pos = ftello(fp);
298  break;
299  }
300  pos = (off64_t)(pos + offs);
301  fseeko(fp, pos, SEEK_SET);
302  if (pos == ftello(fp))
303  return (tsize_t)pos;
304 #else
305  off_t pos = 0;
306  if (!cookie || !fp)
307  return (tsize_t)-1;
308  switch (whence) {
309  case SEEK_SET:
310  pos = 0;
311  break;
312  case SEEK_CUR:
313  pos = ftell(fp);
314  break;
315  case SEEK_END:
316  fseek(fp, 0, SEEK_END);
317  pos = ftell(fp);
318  break;
319  }
320  pos = (off_t)(pos + offs);
321  fseek(fp, pos, SEEK_SET);
322  if (pos == ftell(fp))
323  return (tsize_t)pos;
324 #endif
325  return (tsize_t)-1;
326 }
327 
328 static int
329 lept_close_proc(thandle_t cookie)
330 {
331  FILE* fp = (FILE *)cookie;
332  if (!cookie || !fp)
333  return 0;
334  fseek(fp, 0, SEEK_SET);
335  return 0;
336 }
337 
338 static toff_t
339 lept_size_proc(thandle_t cookie)
340 {
341  FILE* fp = (FILE *)cookie;
342 #if defined(_MSC_VER)
343  __int64 pos;
344  __int64 size;
345  if (!cookie || !fp)
346  return (tsize_t)-1;
347  pos = _ftelli64(fp);
348  _fseeki64(fp, 0, SEEK_END);
349  size = _ftelli64(fp);
350  _fseeki64(fp, pos, SEEK_SET);
351 #elif defined(_LARGEFILE64_SOURCE)
352  off64_t pos;
353  off64_t size;
354  if (!fp)
355  return (tsize_t)-1;
356  pos = ftello(fp);
357  fseeko(fp, 0, SEEK_END);
358  size = ftello(fp);
359  fseeko(fp, pos, SEEK_SET);
360 #else
361  off_t pos;
362  off_t size;
363  if (!cookie || !fp)
364  return (tsize_t)-1;
365  pos = ftell(fp);
366  fseek(fp, 0, SEEK_END);
367  size = ftell(fp);
368  fseek(fp, pos, SEEK_SET);
369 #endif
370  return (toff_t)size;
371 }
372 
373 
374 /*--------------------------------------------------------------*
375  * Reading from file *
376  *--------------------------------------------------------------*/
393 PIX *
394 pixReadTiff(const char *filename,
395  l_int32 n)
396 {
397 FILE *fp;
398 PIX *pix;
399 
400  if (!filename)
401  return (PIX *)ERROR_PTR("filename not defined", __func__, NULL);
402 
403  if ((fp = fopenReadStream(filename)) == NULL)
404  return (PIX *)ERROR_PTR("image file not found", __func__, NULL);
405  pix = pixReadStreamTiff(fp, n);
406  fclose(fp);
407  return pix;
408 }
409 
410 
411 /*--------------------------------------------------------------*
412  * Reading from stream *
413  *--------------------------------------------------------------*/
428 PIX *
430  l_int32 n)
431 {
432 PIX *pix;
433 TIFF *tif;
434 
435  if (!fp)
436  return (PIX *)ERROR_PTR("stream not defined", __func__, NULL);
437 
438  if ((tif = fopenTiff(fp, "r")) == NULL)
439  return (PIX *)ERROR_PTR("tif not opened", __func__, NULL);
440 
441  if (TIFFSetDirectory(tif, n) == 0) {
442  TIFFCleanup(tif);
443  return NULL;
444  }
445  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
446  TIFFCleanup(tif);
447  return NULL;
448  }
449  TIFFCleanup(tif);
450  return pix;
451 }
452 
453 
496 static PIX *
498 {
499 char *text;
500 l_uint8 *linebuf, *data, *rowptr;
501 l_uint16 spp, bps, photometry, tiffcomp, orientation, sample_fmt;
502 l_uint16 *redmap, *greenmap, *bluemap;
503 l_int32 d, wpl, bpl, comptype, i, j, k, ncolors, rval, gval, bval, aval;
504 l_int32 xres, yres, tiffbpl, packedbpl, half_size, twothirds_size;
505 l_uint32 w, h, tiffword, read_oriented;
506 l_uint32 *line, *ppixel, *tiffdata, *pixdata;
507 PIX *pix, *pix1;
508 PIXCMAP *cmap;
509 
510  if (!tif)
511  return (PIX *)ERROR_PTR("tif not defined", __func__, NULL);
512 
513  read_oriented = 0;
514 
515  /* Only accept uint image data:
516  * SAMPLEFORMAT_UINT = 1;
517  * SAMPLEFORMAT_INT = 2;
518  * SAMPLEFORMAT_IEEEFP = 3;
519  * SAMPLEFORMAT_VOID = 4; */
520  TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_fmt);
521  if (sample_fmt != SAMPLEFORMAT_UINT) {
522  L_ERROR("sample format = %d is not uint\n", __func__, sample_fmt);
523  return NULL;
524  }
525 
526  /* Can't read tiff in tiled format. For what is involved, see, e.g:
527  * https://www.cs.rochester.edu/~nelson/courses/vision/\
528  * resources/tiff/libtiff.html#Tiles
529  * A tiled tiff can be converted to a normal (strip) tif:
530  * tiffcp -s <input-tiled-tif> <output-strip-tif> */
531  if (TIFFIsTiled(tif)) {
532  L_ERROR("tiled format is not supported\n", __func__);
533  return NULL;
534  }
535 
536  /* Old style jpeg is not supported. We tried supporting 8 bpp.
537  * TIFFReadScanline() fails on this format, so we used RGBA
538  * reading, which generates a 4 spp image, and pulled out the
539  * red component. However, there were problems with double-frees
540  * in cleanup. For RGB, tiffbpl is exactly half the size that
541  * you would expect for the raster data in a scanline, which
542  * is 3 * w. */
543  TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp);
544  if (tiffcomp == COMPRESSION_OJPEG) {
545  L_ERROR("old style jpeg format is not supported\n", __func__);
546  return NULL;
547  }
548 
549  /* webp in tiff is in 4.1.0 and not yet supported in Adobe registry */
550 #if defined(COMPRESSION_WEBP)
551  if (tiffcomp == COMPRESSION_WEBP) {
552  L_ERROR("webp in tiff not generally supported yet\n", __func__);
553  return NULL;
554  }
555 #endif /* COMPRESSION_WEBP */
556 
557  /* Use default fields for bps and spp */
558  TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
559  TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
560  if (bps != 1 && bps != 2 && bps != 4 && bps != 8 && bps != 16) {
561  L_ERROR("invalid bps = %d\n", __func__, bps);
562  return NULL;
563  }
564  if (spp == 2 && bps != 8) {
565  L_ERROR("for 2 spp, only handle 8 bps; this is %d bps\n",
566  __func__, bps);
567  return NULL;
568  }
569  if ((spp == 3 || spp == 4) && bps < 8) {
570  L_ERROR("for 3 and 4 spp, only handle 8 and 16 bps; this is %d bps\n",
571  __func__, bps);
572  return NULL;
573  }
574  if (spp == 1) {
575  d = bps;
576  } else if (spp == 2) { /* gray plus alpha */
577  d = 32; /* will convert to RGBA */
578  } else if (spp == 3 || spp == 4) {
579  d = 32;
580  } else {
581  L_ERROR("spp = %d; not in {1,2,3,4}\n", __func__, spp);
582  return NULL;
583  }
584 
585  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
586  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
587  if (w > MaxTiffWidth) {
588  L_ERROR("width = %d pixels; too large\n", __func__, w);
589  return NULL;
590  }
591  if (h > MaxTiffHeight) {
592  L_ERROR("height = %d pixels; too large\n", __func__, h);
593  return NULL;
594  }
595 
596  /* The relation between the size of a byte buffer required to hold
597  a raster of image pixels (packedbpl) and the size of the tiff
598  buffer (tiffbuf) is either 1:1 or approximately 1.5:1 or 2:1,
599  depending on how the data is stored and subsampled. For security,
600  we test this relation between tiffbuf and the image parameters
601  w, spp and bps. */
602  tiffbpl = TIFFScanlineSize(tif);
603  packedbpl = (bps * spp * w + 7) / 8;
604  half_size = (L_ABS(2 * tiffbpl - packedbpl) <= 8);
605  twothirds_size = (L_ABS(3 * tiffbpl - 2 * packedbpl) <= 8);
606 #if 0
607  if (half_size)
608  L_INFO("half_size: packedbpl = %d is approx. twice tiffbpl = %d\n",
609  __func__, packedbpl, tiffbpl);
610  if (twothirds_size)
611  L_INFO("twothirds_size: packedbpl = %d is approx. 1.5 tiffbpl = %d\n",
612  __func__, packedbpl, tiffbpl);
613  lept_stderr("tiffbpl = %d, packedbpl = %d, bps = %d, spp = %d, w = %d\n",
614  tiffbpl, packedbpl, bps, spp, w);
615 #endif
616  if (tiffbpl != packedbpl && !half_size && !twothirds_size) {
617  L_ERROR("invalid tiffbpl: tiffbpl = %d, packedbpl = %d, "
618  "bps = %d, spp = %d, w = %d\n",
619  __func__, tiffbpl, packedbpl, bps, spp, w);
620  return NULL;
621  }
622 
623  /* Use a linebuf that will hold all the pixels generated
624  by tiff when reading (decompressing) a scanline. */
625  if ((pix = pixCreate(w, h, d)) == NULL)
626  return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
627  pixSetInputFormat(pix, IFF_TIFF);
628  data = (l_uint8 *)pixGetData(pix);
629  wpl = pixGetWpl(pix);
630  bpl = 4 * wpl;
631  if (spp == 1) {
632  linebuf = (l_uint8 *)LEPT_CALLOC(4 * wpl, sizeof(l_uint8));
633  for (i = 0; i < h; i++) {
634  if (TIFFReadScanline(tif, linebuf, i, 0) < 0) {
635  LEPT_FREE(linebuf);
636  pixDestroy(&pix);
637  L_ERROR("spp = 1, read fail at line %d\n", __func__, i);
638  return NULL;
639  }
640  memcpy(data, linebuf, tiffbpl);
641  data += bpl;
642  }
643  if (bps <= 8)
644  pixEndianByteSwap(pix);
645  else /* bps == 16 */
647  LEPT_FREE(linebuf);
648  } else if (spp == 2 && bps == 8) { /* gray plus alpha */
649  L_INFO("gray+alpha is not supported; converting to RGBA\n", __func__);
650  pixSetSpp(pix, 4);
651  linebuf = (l_uint8 *)LEPT_CALLOC(4 * wpl, sizeof(l_uint8));
652  pixdata = pixGetData(pix);
653  for (i = 0; i < h; i++) {
654  if (TIFFReadScanline(tif, linebuf, i, 0) < 0) {
655  LEPT_FREE(linebuf);
656  pixDestroy(&pix);
657  L_ERROR("spp = 2, read fail at line %d\n", __func__, i);
658  return NULL;
659  }
660  rowptr = linebuf;
661  ppixel = pixdata + i * wpl;
662  for (j = k = 0; j < w; j++) {
663  /* Copy gray value into r, g and b */
664  SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
665  SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
666  SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
667  SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
668  ppixel++;
669  }
670  }
671  LEPT_FREE(linebuf);
672  } else { /* rgb and rgba */
673  if ((tiffdata = (l_uint32 *)LEPT_CALLOC((size_t)w * h,
674  sizeof(l_uint32))) == NULL) {
675  pixDestroy(&pix);
676  return (PIX *)ERROR_PTR("calloc fail for tiffdata", __func__, NULL);
677  }
678  /* TIFFReadRGBAImageOriented() converts to 8 bps */
679  if (!TIFFReadRGBAImageOriented(tif, w, h, tiffdata,
680  ORIENTATION_TOPLEFT, 0)) {
681  LEPT_FREE(tiffdata);
682  pixDestroy(&pix);
683  return (PIX *)ERROR_PTR("failed to read tiffdata", __func__, NULL);
684  } else {
685  read_oriented = 1;
686  }
687 
688  if (spp == 4) pixSetSpp(pix, 4);
689  line = pixGetData(pix);
690  for (i = 0; i < h; i++, line += wpl) {
691  for (j = 0, ppixel = line; j < w; j++) {
692  /* TIFFGet* are macros */
693  tiffword = tiffdata[i * w + j];
694  rval = TIFFGetR(tiffword);
695  gval = TIFFGetG(tiffword);
696  bval = TIFFGetB(tiffword);
697  if (spp == 3) {
698  composeRGBPixel(rval, gval, bval, ppixel);
699  } else { /* spp == 4 */
700  aval = TIFFGetA(tiffword);
701  composeRGBAPixel(rval, gval, bval, aval, ppixel);
702  }
703  ppixel++;
704  }
705  }
706  LEPT_FREE(tiffdata);
707  }
708 
709  if (getTiffStreamResolution(tif, &xres, &yres) == 0) {
710  pixSetXRes(pix, xres);
711  pixSetYRes(pix, yres);
712  }
713 
714  /* Find and save the compression type */
715  comptype = getTiffCompressedFormat(tiffcomp);
716  pixSetInputFormat(pix, comptype);
717 
718  if (TIFFGetField(tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap)) {
719  /* Save the colormap as a pix cmap. Because the
720  * tiff colormap components are 16 bit unsigned,
721  * and go from black (0) to white (0xffff), the
722  * the pix cmap takes the most significant byte. */
723  if (bps > 8) {
724  pixDestroy(&pix);
725  return (PIX *)ERROR_PTR("colormap size > 256", __func__, NULL);
726  }
727  if ((cmap = pixcmapCreate(bps)) == NULL) {
728  pixDestroy(&pix);
729  return (PIX *)ERROR_PTR("colormap not made", __func__, NULL);
730  }
731  ncolors = 1 << bps;
732  for (i = 0; i < ncolors; i++)
733  pixcmapAddColor(cmap, redmap[i] >> 8, greenmap[i] >> 8,
734  bluemap[i] >> 8);
735  if (pixSetColormap(pix, cmap)) {
736  pixDestroy(&pix);
737  return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL);
738  }
739 
740  /* Remove the colormap for 1 bpp. */
741  if (bps == 1) {
743  pixDestroy(&pix);
744  pix = pix1;
745  }
746  } else { /* No colormap: check photometry and invert if necessary */
747  if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometry)) {
748  /* Guess default photometry setting. Assume min_is_white
749  * if compressed 1 bpp; min_is_black otherwise. */
750  if (tiffcomp == COMPRESSION_CCITTFAX3 ||
751  tiffcomp == COMPRESSION_CCITTFAX4 ||
752  tiffcomp == COMPRESSION_CCITTRLE ||
753  tiffcomp == COMPRESSION_CCITTRLEW) {
754  photometry = PHOTOMETRIC_MINISWHITE;
755  } else {
756  photometry = PHOTOMETRIC_MINISBLACK;
757  }
758  }
759  if ((d == 1 && photometry == PHOTOMETRIC_MINISBLACK) ||
760  (d == 8 && photometry == PHOTOMETRIC_MINISWHITE))
761  pixInvert(pix, pix);
762  }
763 
764  if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation)) {
765  if (orientation >= 1 && orientation <= 8) {
766  struct tiff_transform *transform = (read_oriented) ?
767  &tiff_partial_orientation_transforms[orientation - 1] :
768  &tiff_orientation_transforms[orientation - 1];
769  if (transform->vflip) pixFlipTB(pix, pix);
770  if (transform->hflip) pixFlipLR(pix, pix);
771  if (transform->rotate) {
772  PIX *oldpix = pix;
773  pix = pixRotate90(oldpix, transform->rotate);
774  pixDestroy(&oldpix);
775  }
776  }
777  }
778 
779  text = NULL;
780  TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &text);
781  if (text) pixSetText(pix, text);
782  return pix;
783 }
784 
785 
786 /*--------------------------------------------------------------*
787  * Writing to file *
788  *--------------------------------------------------------------*/
810 l_ok
811 pixWriteTiff(const char *filename,
812  PIX *pix,
813  l_int32 comptype,
814  const char *modestr)
815 {
816  return pixWriteTiffCustom(filename, pix, comptype, modestr,
817  NULL, NULL, NULL, NULL);
818 }
819 
820 
867 l_ok
868 pixWriteTiffCustom(const char *filename,
869  PIX *pix,
870  l_int32 comptype,
871  const char *modestr,
872  NUMA *natags,
873  SARRAY *savals,
874  SARRAY *satypes,
875  NUMA *nasizes)
876 {
877 l_int32 ret;
878 TIFF *tif;
879 
880  if (!filename)
881  return ERROR_INT("filename not defined", __func__, 1);
882  if (!pix)
883  return ERROR_INT("pix not defined", __func__, 1);
884 
885  if ((tif = openTiff(filename, modestr)) == NULL)
886  return ERROR_INT("tif not opened", __func__, 1);
887  ret = pixWriteToTiffStream(tif, pix, comptype, natags, savals,
888  satypes, nasizes);
889  TIFFClose(tif);
890  return ret;
891 }
892 
893 
894 /*--------------------------------------------------------------*
895  * Writing to stream *
896  *--------------------------------------------------------------*/
924 l_ok
926  PIX *pix,
927  l_int32 comptype)
928 {
929  return pixWriteStreamTiffWA(fp, pix, comptype, "w");
930 }
931 
932 
949 l_ok
951  PIX *pix,
952  l_int32 comptype,
953  const char *modestr)
954 {
955 TIFF *tif;
956 
957  if (!fp)
958  return ERROR_INT("stream not defined", __func__, 1 );
959  if (!pix)
960  return ERROR_INT("pix not defined", __func__, 1 );
961  if (strcmp(modestr, "w") && strcmp(modestr, "a")) {
962  L_ERROR("modestr = %s; not 'w' or 'a'\n", __func__, modestr);
963  return 1;
964  }
965 
966  if (pixGetDepth(pix) != 1 && comptype != IFF_TIFF &&
967  comptype != IFF_TIFF_LZW && comptype != IFF_TIFF_ZIP &&
968  comptype != IFF_TIFF_JPEG) {
969  L_WARNING("invalid compression type %d for bpp > 1; using TIFF_ZIP\n",
970  __func__, comptype);
971  comptype = IFF_TIFF_ZIP;
972  }
973 
974  if ((tif = fopenTiff(fp, modestr)) == NULL)
975  return ERROR_INT("tif not opened", __func__, 1);
976 
977  if (pixWriteToTiffStream(tif, pix, comptype, NULL, NULL, NULL, NULL)) {
978  TIFFCleanup(tif);
979  return ERROR_INT("tif write error", __func__, 1);
980  }
981 
982  TIFFCleanup(tif);
983  return 0;
984 }
985 
986 
1022 static l_int32
1024  PIX *pix,
1025  l_int32 comptype,
1026  NUMA *natags,
1027  SARRAY *savals,
1028  SARRAY *satypes,
1029  NUMA *nasizes)
1030 {
1031 l_uint8 *linebuf, *data;
1032 l_uint16 redmap[256], greenmap[256], bluemap[256];
1033 l_int32 w, h, d, spp, i, j, k, wpl, bpl, tiffbpl, ncolors, cmapsize;
1034 l_int32 *rmap, *gmap, *bmap;
1035 l_int32 xres, yres;
1036 l_uint32 *line, *ppixel;
1037 PIX *pixt;
1038 PIXCMAP *cmap;
1039 char *text;
1040 
1041  if (!tif)
1042  return ERROR_INT("tif stream not defined", __func__, 1);
1043  if (!pix)
1044  return ERROR_INT( "pix not defined", __func__, 1 );
1045 
1046  pixSetPadBits(pix, 0);
1047  pixGetDimensions(pix, &w, &h, &d);
1048  spp = pixGetSpp(pix);
1049  xres = pixGetXRes(pix);
1050  yres = pixGetYRes(pix);
1051  if (xres == 0) xres = DefaultResolution;
1052  if (yres == 0) yres = DefaultResolution;
1053 
1054  /* ------------------ Write out the header ------------- */
1055  TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (l_uint32)RESUNIT_INCH);
1056  TIFFSetField(tif, TIFFTAG_XRESOLUTION, (l_float64)xres);
1057  TIFFSetField(tif, TIFFTAG_YRESOLUTION, (l_float64)yres);
1058 
1059  TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (l_uint32)w);
1060  TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (l_uint32)h);
1061  TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
1062 
1063  if ((text = pixGetText(pix)) != NULL)
1064  TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, text);
1065 
1066  if (d == 1 && !pixGetColormap(pix)) {
1067  /* If d == 1, preserve the colormap. Note that when
1068  * d == 1 pix with colormaps are read, the colormaps
1069  * are removed. The only pix in leptonica that have
1070  * colormaps are made programmatically. */
1071  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
1072  } else if ((d == 32 && spp == 3) || d == 24) {
1073  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
1074  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)3);
1075  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,
1076  (l_uint16)8, (l_uint16)8, (l_uint16)8);
1077  } else if (d == 32 && spp == 4) {
1078  l_uint16 val[1];
1079  val[0] = EXTRASAMPLE_ASSOCALPHA;
1080  TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, (l_uint16)1, &val);
1081  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
1082  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)4);
1083  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,
1084  (l_uint16)8, (l_uint16)8, (l_uint16)8, (l_uint16)8);
1085  } else if (d == 16) { /* we only support spp = 1, bps = 16 */
1086  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
1087  } else if ((cmap = pixGetColormap(pix)) == NULL) {
1088  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
1089  } else { /* Save colormap in the tiff; not more than 256 colors */
1090  if (d > 8) {
1091  L_ERROR("d = %d > 8 with colormap!; reducing to 8\n", __func__, d);
1092  d = 8;
1093  }
1094  pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL);
1095  ncolors = pixcmapGetCount(cmap);
1096  ncolors = L_MIN(256, ncolors); /* max 256 */
1097  cmapsize = 1 << d;
1098  cmapsize = L_MIN(256, cmapsize); /* power of 2; max 256 */
1099  if (ncolors > cmapsize) {
1100  L_WARNING("too many colors in cmap for tiff; truncating\n",
1101  __func__);
1102  ncolors = cmapsize;
1103  }
1104  for (i = 0; i < ncolors; i++) {
1105  redmap[i] = (rmap[i] << 8) | rmap[i];
1106  greenmap[i] = (gmap[i] << 8) | gmap[i];
1107  bluemap[i] = (bmap[i] << 8) | bmap[i];
1108  }
1109  for (i = ncolors; i < cmapsize; i++) /* init, even though not used */
1110  redmap[i] = greenmap[i] = bluemap[i] = 0;
1111  LEPT_FREE(rmap);
1112  LEPT_FREE(gmap);
1113  LEPT_FREE(bmap);
1114 
1115  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
1116  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)1);
1117  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (l_uint16)d);
1118  TIFFSetField(tif, TIFFTAG_COLORMAP, redmap, greenmap, bluemap);
1119  }
1120 
1121  if (d <= 16) {
1122  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (l_uint16)d);
1123  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (l_uint16)1);
1124  }
1125 
1126  TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
1127  if (comptype == IFF_TIFF) { /* no compression */
1128  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
1129  } else if (comptype == IFF_TIFF_G4) {
1130  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4);
1131  } else if (comptype == IFF_TIFF_G3) {
1132  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
1133  } else if (comptype == IFF_TIFF_RLE) {
1134  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
1135  } else if (comptype == IFF_TIFF_PACKBITS) {
1136  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
1137  } else if (comptype == IFF_TIFF_LZW) {
1138  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
1139  } else if (comptype == IFF_TIFF_ZIP) {
1140  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
1141  } else if (comptype == IFF_TIFF_JPEG) {
1142  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
1143  } else {
1144  L_WARNING("unknown tiff compression; using none\n", __func__);
1145  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
1146  }
1147 
1148  /* This is a no-op if arrays are NULL */
1149  writeCustomTiffTags(tif, natags, savals, satypes, nasizes);
1150 
1151  /* ------------- Write out the image data ------------- */
1152  tiffbpl = TIFFScanlineSize(tif);
1153  wpl = pixGetWpl(pix);
1154  bpl = 4 * wpl;
1155  if (tiffbpl > bpl)
1156  lept_stderr("Big trouble: tiffbpl = %d, bpl = %d\n", tiffbpl, bpl);
1157  if ((linebuf = (l_uint8 *)LEPT_CALLOC(1, bpl)) == NULL)
1158  return ERROR_INT("calloc fail for linebuf", __func__, 1);
1159 
1160  /* Use single strip for image */
1161  TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, h);
1162 
1163  if (d != 24 && d != 32) {
1164  if (d == 16)
1165  pixt = pixEndianTwoByteSwapNew(pix);
1166  else
1167  pixt = pixEndianByteSwapNew(pix);
1168  data = (l_uint8 *)pixGetData(pixt);
1169  for (i = 0; i < h; i++, data += bpl) {
1170  memcpy(linebuf, data, tiffbpl);
1171  if (TIFFWriteScanline(tif, linebuf, i, 0) < 0)
1172  break;
1173  }
1174  pixDestroy(&pixt);
1175  } else if (d == 24) { /* See note 4 above: special case of 24 bpp rgb */
1176  for (i = 0; i < h; i++) {
1177  line = pixGetData(pix) + i * wpl;
1178  if (TIFFWriteScanline(tif, (l_uint8 *)line, i, 0) < 0)
1179  break;
1180  }
1181  } else { /* 32 bpp rgb or rgba */
1182  for (i = 0; i < h; i++) {
1183  line = pixGetData(pix) + i * wpl;
1184  for (j = 0, k = 0, ppixel = line; j < w; j++) {
1185  linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
1186  linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
1187  linebuf[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
1188  if (spp == 4)
1189  linebuf[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
1190  ppixel++;
1191  }
1192  if (TIFFWriteScanline(tif, linebuf, i, 0) < 0)
1193  break;
1194  }
1195  }
1196 
1197 /* TIFFWriteDirectory(tif); */
1198  LEPT_FREE(linebuf);
1199 
1200  return 0;
1201 }
1202 
1203 
1233 static l_int32
1235  NUMA *natags,
1236  SARRAY *savals,
1237  SARRAY *satypes,
1238  NUMA *nasizes)
1239 {
1240 char *sval, *type;
1241 l_int32 i, n, ns, size, tagval, val;
1242 l_float64 dval;
1243 l_uint32 uval, uval2;
1244 
1245  if (!tif)
1246  return ERROR_INT("tif stream not defined", __func__, 1);
1247  if (!natags && !savals && !satypes)
1248  return 0;
1249  if (!natags || !savals || !satypes)
1250  return ERROR_INT("not all arrays defined", __func__, 1);
1251  n = numaGetCount(natags);
1252  if ((sarrayGetCount(savals) != n) || (sarrayGetCount(satypes) != n))
1253  return ERROR_INT("not all sa the same size", __func__, 1);
1254 
1255  /* The sized arrays (4 args to TIFFSetField) are written first */
1256  if (nasizes) {
1257  ns = numaGetCount(nasizes);
1258  if (ns > n)
1259  return ERROR_INT("too many 4-arg tag calls", __func__, 1);
1260  for (i = 0; i < ns; i++) {
1261  numaGetIValue(natags, i, &tagval);
1262  sval = sarrayGetString(savals, i, L_NOCOPY);
1263  type = sarrayGetString(satypes, i, L_NOCOPY);
1264  numaGetIValue(nasizes, i, &size);
1265  if (strcmp(type, "char*") && strcmp(type, "l_uint8*"))
1266  L_WARNING("array type not char* or l_uint8*; ignore\n",
1267  __func__);
1268  TIFFSetField(tif, tagval, size, sval);
1269  }
1270  } else {
1271  ns = 0;
1272  }
1273 
1274  /* The typical tags (3 args to TIFFSetField) are now written */
1275  for (i = ns; i < n; i++) {
1276  numaGetIValue(natags, i, &tagval);
1277  sval = sarrayGetString(savals, i, L_NOCOPY);
1278  type = sarrayGetString(satypes, i, L_NOCOPY);
1279  if (!strcmp(type, "char*") || !strcmp(type, "const char*")) {
1280  TIFFSetField(tif, tagval, sval);
1281  } else if (!strcmp(type, "l_uint16")) {
1282  if (sscanf(sval, "%u", &uval) == 1) {
1283  TIFFSetField(tif, tagval, (l_uint16)uval);
1284  } else {
1285  lept_stderr("val %s not of type %s\n", sval, type);
1286  return ERROR_INT("custom tag(s) not written", __func__, 1);
1287  }
1288  } else if (!strcmp(type, "l_uint32")) {
1289  if (sscanf(sval, "%u", &uval) == 1) {
1290  TIFFSetField(tif, tagval, uval);
1291  } else {
1292  lept_stderr("val %s not of type %s\n", sval, type);
1293  return ERROR_INT("custom tag(s) not written", __func__, 1);
1294  }
1295  } else if (!strcmp(type, "l_int32")) {
1296  if (sscanf(sval, "%d", &val) == 1) {
1297  TIFFSetField(tif, tagval, val);
1298  } else {
1299  lept_stderr("val %s not of type %s\n", sval, type);
1300  return ERROR_INT("custom tag(s) not written", __func__, 1);
1301  }
1302  } else if (!strcmp(type, "l_float64")) {
1303  if (sscanf(sval, "%lf", &dval) == 1) {
1304  TIFFSetField(tif, tagval, dval);
1305  } else {
1306  lept_stderr("val %s not of type %s\n", sval, type);
1307  return ERROR_INT("custom tag(s) not written", __func__, 1);
1308  }
1309  } else if (!strcmp(type, "l_uint16-l_uint16")) {
1310  if (sscanf(sval, "%u-%u", &uval, &uval2) == 2) {
1311  TIFFSetField(tif, tagval, (l_uint16)uval, (l_uint16)uval2);
1312  } else {
1313  lept_stderr("val %s not of type %s\n", sval, type);
1314  return ERROR_INT("custom tag(s) not written", __func__, 1);
1315  }
1316  } else {
1317  lept_stderr("unknown type %s\n",type);
1318  return ERROR_INT("unknown type; tag(s) not written", __func__, 1);
1319  }
1320  }
1321  return 0;
1322 }
1323 
1324 
1325 /*--------------------------------------------------------------*
1326  * Reading and writing multipage tiff *
1327  *--------------------------------------------------------------*/
1359 PIX *
1360 pixReadFromMultipageTiff(const char *fname,
1361  size_t *poffset)
1362 {
1363 l_int32 retval;
1364 size_t offset;
1365 PIX *pix;
1366 TIFF *tif;
1367 
1368  if (!fname)
1369  return (PIX *)ERROR_PTR("fname not defined", __func__, NULL);
1370  if (!poffset)
1371  return (PIX *)ERROR_PTR("&offset not defined", __func__, NULL);
1372 
1373  if ((tif = openTiff(fname, "r")) == NULL) {
1374  L_ERROR("tif open failed for %s\n", __func__, fname);
1375  return NULL;
1376  }
1377 
1378  /* Set ptrs in the TIFF to the beginning of the image */
1379  offset = *poffset;
1380  retval = (offset == 0) ? TIFFSetDirectory(tif, 0)
1381  : TIFFSetSubDirectory(tif, offset);
1382  if (retval == 0) {
1383  TIFFClose(tif);
1384  return NULL;
1385  }
1386 
1387  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
1388  TIFFClose(tif);
1389  return NULL;
1390  }
1391 
1392  /* Advance to the next image and return the new offset */
1393  TIFFReadDirectory(tif);
1394  *poffset = TIFFCurrentDirOffset(tif);
1395  TIFFClose(tif);
1396  return pix;
1397 }
1398 
1399 
1406 PIXA *
1407 pixaReadMultipageTiff(const char *filename)
1408 {
1409 l_int32 i, npages;
1410 FILE *fp;
1411 PIX *pix;
1412 PIXA *pixa;
1413 TIFF *tif;
1414 
1415  if (!filename)
1416  return (PIXA *)ERROR_PTR("filename not defined", __func__, NULL);
1417 
1418  if ((fp = fopenReadStream(filename)) == NULL)
1419  return (PIXA *)ERROR_PTR("stream not opened", __func__, NULL);
1420  if (fileFormatIsTiff(fp)) {
1421  tiffGetCount(fp, &npages);
1422  L_INFO(" Tiff: %d pages\n", __func__, npages);
1423  } else {
1424  return (PIXA *)ERROR_PTR("file not tiff", __func__, NULL);
1425  }
1426 
1427  if ((tif = fopenTiff(fp, "r")) == NULL)
1428  return (PIXA *)ERROR_PTR("tif not opened", __func__, NULL);
1429 
1430  pixa = pixaCreate(npages);
1431  pix = NULL;
1432  for (i = 0; i < npages; i++) {
1433  if ((pix = pixReadFromTiffStream(tif)) != NULL) {
1434  pixaAddPix(pixa, pix, L_INSERT);
1435  } else {
1436  L_WARNING("pix not read for page %d\n", __func__, i);
1437  }
1438 
1439  /* Advance to the next directory (i.e., the next image) */
1440  if (TIFFReadDirectory(tif) == 0)
1441  break;
1442  }
1443 
1444  fclose(fp);
1445  TIFFCleanup(tif);
1446  return pixa;
1447 }
1448 
1449 
1464 l_ok
1465 pixaWriteMultipageTiff(const char *fname,
1466  PIXA *pixa)
1467 {
1468 const char *modestr;
1469 l_int32 i, n;
1470 PIX *pix1;
1471 
1472  if (!fname)
1473  return ERROR_INT("fname not defined", __func__, 1);
1474  if (!pixa)
1475  return ERROR_INT("pixa not defined", __func__, 1);
1476 
1477  n = pixaGetCount(pixa);
1478  for (i = 0; i < n; i++) {
1479  modestr = (i == 0) ? "w" : "a";
1480  pix1 = pixaGetPix(pixa, i, L_CLONE);
1481  if (pixGetDepth(pix1) == 1)
1482  pixWriteTiff(fname, pix1, IFF_TIFF_G4, modestr);
1483  else
1484  pixWriteTiff(fname, pix1, IFF_TIFF_ZIP, modestr);
1485  pixDestroy(&pix1);
1486  }
1487 
1488  return 0;
1489 }
1490 
1491 
1516 l_ok
1517 writeMultipageTiff(const char *dirin,
1518  const char *substr,
1519  const char *fileout)
1520 {
1521 SARRAY *sa;
1522 
1523  if (!dirin)
1524  return ERROR_INT("dirin not defined", __func__, 1);
1525  if (!fileout)
1526  return ERROR_INT("fileout not defined", __func__, 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  if (!sa)
1560  return ERROR_INT("sa not defined", __func__, 1);
1561  if (!fileout)
1562  return ERROR_INT("fileout not defined", __func__, 1);
1563 
1564  nfiles = sarrayGetCount(sa);
1565  firstfile = TRUE;
1566  for (i = 0; i < nfiles; i++) {
1567  op = (firstfile) ? "w" : "a";
1568  fname = sarrayGetString(sa, i, L_NOCOPY);
1569  findFileFormat(fname, &format);
1570  if (format == IFF_UNKNOWN) {
1571  L_INFO("format of %s not known\n", __func__, fname);
1572  continue;
1573  }
1574 
1575  if ((pix = pixRead(fname)) == NULL) {
1576  L_WARNING("pix not made for file: %s\n", __func__, fname);
1577  continue;
1578  }
1579  if (pixGetDepth(pix) == 1)
1580  pixWriteTiff(fileout, pix, IFF_TIFF_G4, op);
1581  else
1582  pixWriteTiff(fileout, pix, IFF_TIFF_ZIP, op);
1583  firstfile = FALSE;
1584  pixDestroy(&pix);
1585  }
1586 
1587  return 0;
1588 }
1589 
1590 
1591 /*--------------------------------------------------------------*
1592  * Print info to stream *
1593  *--------------------------------------------------------------*/
1601 l_ok
1602 fprintTiffInfo(FILE *fpout,
1603  const char *tiffile)
1604 {
1605 TIFF *tif;
1606 
1607  if (!tiffile)
1608  return ERROR_INT("tiffile not defined", __func__, 1);
1609  if (!fpout)
1610  return ERROR_INT("stream out not defined", __func__, 1);
1611 
1612  if ((tif = openTiff(tiffile, "rb")) == NULL)
1613  return ERROR_INT("tif not open for read", __func__, 1);
1614 
1615  TIFFPrintDirectory(tif, fpout, 0);
1616  TIFFClose(tif);
1617 
1618  return 0;
1619 }
1620 
1621 
1622 /*--------------------------------------------------------------*
1623  * Get page count *
1624  *--------------------------------------------------------------*/
1632 l_ok
1633 tiffGetCount(FILE *fp,
1634  l_int32 *pn)
1635 {
1636 l_int32 i;
1637 TIFF *tif;
1638 
1639  if (!fp)
1640  return ERROR_INT("stream not defined", __func__, 1);
1641  if (!pn)
1642  return ERROR_INT("&n not defined", __func__, 1);
1643  *pn = 0;
1644 
1645  if ((tif = fopenTiff(fp, "r")) == NULL)
1646  return ERROR_INT("tif not open for read", __func__, 1);
1647 
1648  for (i = 1; ; i++) {
1649  if (TIFFReadDirectory(tif) == 0)
1650  break;
1651  if (i == ManyPagesInTiffFile + 1) {
1652  L_WARNING("big file: more than %d pages\n", __func__,
1653  ManyPagesInTiffFile);
1654  }
1655  }
1656  *pn = i;
1657  TIFFCleanup(tif);
1658  return 0;
1659 }
1660 
1661 
1662 /*--------------------------------------------------------------*
1663  * Get resolution from tif *
1664  *--------------------------------------------------------------*/
1678 l_ok
1680  l_int32 *pxres,
1681  l_int32 *pyres)
1682 {
1683 TIFF *tif;
1684 
1685  if (!pxres || !pyres)
1686  return ERROR_INT("&xres and &yres not both defined", __func__, 1);
1687  *pxres = *pyres = 0;
1688  if (!fp)
1689  return ERROR_INT("stream not opened", __func__, 1);
1690 
1691  if ((tif = fopenTiff(fp, "r")) == NULL)
1692  return ERROR_INT("tif not open for read", __func__, 1);
1693  getTiffStreamResolution(tif, pxres, pyres);
1694  TIFFCleanup(tif);
1695  return 0;
1696 }
1697 
1698 
1712 static l_int32
1714  l_int32 *pxres,
1715  l_int32 *pyres)
1716 {
1717 l_uint16 resunit;
1718 l_int32 foundxres, foundyres;
1719 l_float32 fxres, fyres;
1720 
1721  if (!tif)
1722  return ERROR_INT("tif not opened", __func__, 1);
1723  if (!pxres || !pyres)
1724  return ERROR_INT("&xres and &yres not both defined", __func__, 1);
1725  *pxres = *pyres = 0;
1726 
1727  TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
1728  foundxres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &fxres);
1729  foundyres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &fyres);
1730  if (!foundxres && !foundyres) return 1;
1731  if (isnan(fxres) || isnan(fyres)) return 1;
1732  if (!foundxres && foundyres)
1733  fxres = fyres;
1734  else if (foundxres && !foundyres)
1735  fyres = fxres;
1736 
1737  /* Avoid overflow into int32; set max fxres and fyres to 5 x 10^8 */
1738  if (fxres < 0 || fxres > (1L << 29) || fyres < 0 || fyres > (1L << 29))
1739  return ERROR_INT("fxres and/or fyres values are invalid", __func__, 1);
1740 
1741  if (resunit == RESUNIT_CENTIMETER) { /* convert to ppi */
1742  *pxres = (l_int32)(2.54 * fxres + 0.5);
1743  *pyres = (l_int32)(2.54 * fyres + 0.5);
1744  } else {
1745  *pxres = (l_int32)(fxres + 0.5);
1746  *pyres = (l_int32)(fyres + 0.5);
1747  }
1748 
1749  return 0;
1750 }
1751 
1752 
1753 /*--------------------------------------------------------------*
1754  * Get some tiff header information *
1755  *--------------------------------------------------------------*/
1776 l_ok
1777 readHeaderTiff(const char *filename,
1778  l_int32 n,
1779  l_int32 *pw,
1780  l_int32 *ph,
1781  l_int32 *pbps,
1782  l_int32 *pspp,
1783  l_int32 *pres,
1784  l_int32 *pcmap,
1785  l_int32 *pformat)
1786 {
1787 l_int32 ret;
1788 FILE *fp;
1789 
1790  if (pw) *pw = 0;
1791  if (ph) *ph = 0;
1792  if (pbps) *pbps = 0;
1793  if (pspp) *pspp = 0;
1794  if (pres) *pres = 0;
1795  if (pcmap) *pcmap = 0;
1796  if (pformat) *pformat = 0;
1797  if (!filename)
1798  return ERROR_INT("filename not defined", __func__, 1);
1799  if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat)
1800  return ERROR_INT("no results requested", __func__, 1);
1801 
1802  if ((fp = fopenReadStream(filename)) == NULL)
1803  return ERROR_INT("image file not found", __func__, 1);
1804  ret = freadHeaderTiff(fp, n, pw, ph, pbps, pspp, pres, pcmap, pformat);
1805  fclose(fp);
1806  return ret;
1807 }
1808 
1809 
1830 l_ok
1832  l_int32 n,
1833  l_int32 *pw,
1834  l_int32 *ph,
1835  l_int32 *pbps,
1836  l_int32 *pspp,
1837  l_int32 *pres,
1838  l_int32 *pcmap,
1839  l_int32 *pformat)
1840 {
1841 l_int32 i, ret, format;
1842 TIFF *tif;
1843 
1844  if (pw) *pw = 0;
1845  if (ph) *ph = 0;
1846  if (pbps) *pbps = 0;
1847  if (pspp) *pspp = 0;
1848  if (pres) *pres = 0;
1849  if (pcmap) *pcmap = 0;
1850  if (pformat) *pformat = 0;
1851  if (!fp)
1852  return ERROR_INT("stream not defined", __func__, 1);
1853  if (n < 0)
1854  return ERROR_INT("image index must be >= 0", __func__, 1);
1855  if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat)
1856  return ERROR_INT("no results requested", __func__, 1);
1857 
1858  findFileFormatStream(fp, &format);
1859  if (!L_FORMAT_IS_TIFF(format))
1860  return ERROR_INT("file not tiff format", __func__, 1);
1861 
1862  if ((tif = fopenTiff(fp, "r")) == NULL)
1863  return ERROR_INT("tif not open for read", __func__, 1);
1864 
1865  for (i = 0; i < n; i++) {
1866  if (TIFFReadDirectory(tif) == 0)
1867  return ERROR_INT("image n not found in file", __func__, 1);
1868  }
1869 
1870  ret = tiffReadHeaderTiff(tif, pw, ph, pbps, pspp, pres, pcmap, pformat);
1871  TIFFCleanup(tif);
1872  return ret;
1873 }
1874 
1875 
1897 l_ok
1898 readHeaderMemTiff(const l_uint8 *cdata,
1899  size_t size,
1900  l_int32 n,
1901  l_int32 *pw,
1902  l_int32 *ph,
1903  l_int32 *pbps,
1904  l_int32 *pspp,
1905  l_int32 *pres,
1906  l_int32 *pcmap,
1907  l_int32 *pformat)
1908 {
1909 l_uint8 *data;
1910 l_int32 i, ret;
1911 TIFF *tif;
1912 
1913  if (pw) *pw = 0;
1914  if (ph) *ph = 0;
1915  if (pbps) *pbps = 0;
1916  if (pspp) *pspp = 0;
1917  if (pres) *pres = 0;
1918  if (pcmap) *pcmap = 0;
1919  if (pformat) *pformat = 0;
1920  if (!pw && !ph && !pbps && !pspp && !pres && !pcmap && !pformat)
1921  return ERROR_INT("no results requested", __func__, 1);
1922  if (!cdata)
1923  return ERROR_INT("cdata not defined", __func__, 1);
1924 
1925  /* Open a tiff stream to memory */
1926  data = (l_uint8 *)cdata; /* we're really not going to change this */
1927  if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL)
1928  return ERROR_INT("tiff stream not opened", __func__, 1);
1929 
1930  for (i = 0; i < n; i++) {
1931  if (TIFFReadDirectory(tif) == 0) {
1932  TIFFClose(tif);
1933  return ERROR_INT("image n not found in file", __func__, 1);
1934  }
1935  }
1936 
1937  ret = tiffReadHeaderTiff(tif, pw, ph, pbps, pspp, pres, pcmap, pformat);
1938  TIFFClose(tif);
1939  return ret;
1940 }
1941 
1942 
1956 static l_int32
1958  l_int32 *pw,
1959  l_int32 *ph,
1960  l_int32 *pbps,
1961  l_int32 *pspp,
1962  l_int32 *pres,
1963  l_int32 *pcmap,
1964  l_int32 *pformat)
1965 {
1966 l_uint16 tiffcomp;
1967 l_uint16 bps, spp;
1968 l_uint16 *rmap, *gmap, *bmap;
1969 l_int32 xres, yres;
1970 l_uint32 w, h;
1971 
1972  if (!tif)
1973  return ERROR_INT("tif not opened", __func__, 1);
1974 
1975  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
1976  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
1977  TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps);
1978  TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
1979  if (w < 1 || h < 1)
1980  return ERROR_INT("tif w and h not both > 0", __func__, 1);
1981  if (bps != 1 && bps != 2 && bps != 4 && bps != 8 && bps != 16)
1982  return ERROR_INT("bps not in set {1,2,4,8,16}", __func__, 1);
1983  if (spp != 1 && spp != 2 && spp != 3 && spp != 4)
1984  return ERROR_INT("spp not in set {1,2,3,4}", __func__, 1);
1985  if (pw) *pw = w;
1986  if (ph) *ph = h;
1987  if (pbps) *pbps = bps;
1988  if (pspp) *pspp = spp;
1989  if (pres) {
1990  if (getTiffStreamResolution(tif, &xres, &yres) == 0)
1991  *pres = (l_int32)xres;
1992  }
1993  if (pcmap) {
1994  if (TIFFGetField(tif, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap))
1995  *pcmap = 1;
1996  }
1997  if (pformat) {
1998  TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp);
1999  *pformat = getTiffCompressedFormat(tiffcomp);
2000  }
2001  return 0;
2002 }
2003 
2004 
2024 l_ok
2026  l_int32 *pcomptype)
2027 {
2028 l_uint16 tiffcomp;
2029 TIFF *tif;
2030 
2031  if (!pcomptype)
2032  return ERROR_INT("&comptype not defined", __func__, 1);
2033  *pcomptype = IFF_UNKNOWN; /* init */
2034  if (!fp)
2035  return ERROR_INT("stream not defined", __func__, 1);
2036 
2037  if ((tif = fopenTiff(fp, "r")) == NULL)
2038  return ERROR_INT("tif not opened", __func__, 1);
2039  TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffcomp);
2040  *pcomptype = getTiffCompressedFormat(tiffcomp);
2041  TIFFCleanup(tif);
2042  return 0;
2043 }
2044 
2045 
2060 static l_int32
2061 getTiffCompressedFormat(l_uint16 tiffcomp)
2062 {
2063 l_int32 comptype;
2064 
2065  switch (tiffcomp)
2066  {
2067  case COMPRESSION_CCITTFAX4:
2068  comptype = IFF_TIFF_G4;
2069  break;
2070  case COMPRESSION_CCITTFAX3:
2071  comptype = IFF_TIFF_G3;
2072  break;
2073  case COMPRESSION_CCITTRLE:
2074  comptype = IFF_TIFF_RLE;
2075  break;
2076  case COMPRESSION_PACKBITS:
2077  comptype = IFF_TIFF_PACKBITS;
2078  break;
2079  case COMPRESSION_LZW:
2080  comptype = IFF_TIFF_LZW;
2081  break;
2082  case COMPRESSION_ADOBE_DEFLATE:
2083  comptype = IFF_TIFF_ZIP;
2084  break;
2085  case COMPRESSION_JPEG:
2086  comptype = IFF_TIFF_JPEG;
2087  break;
2088  default:
2089  comptype = IFF_TIFF;
2090  break;
2091  }
2092  return comptype;
2093 }
2094 
2095 
2096 /*--------------------------------------------------------------*
2097  * Extraction of tiff g4 data *
2098  *--------------------------------------------------------------*/
2110 l_ok
2111 extractG4DataFromFile(const char *filein,
2112  l_uint8 **pdata,
2113  size_t *pnbytes,
2114  l_int32 *pw,
2115  l_int32 *ph,
2116  l_int32 *pminisblack)
2117 {
2118 l_uint8 *inarray, *data;
2119 l_uint16 minisblack, comptype; /* accessors require l_uint16 */
2120 l_int32 istiff;
2121 l_uint32 w, h, rowsperstrip; /* accessors require l_uint32 */
2122 l_uint32 diroff;
2123 size_t fbytes, nbytes;
2124 FILE *fpin;
2125 TIFF *tif;
2126 
2127  if (!pdata)
2128  return ERROR_INT("&data not defined", __func__, 1);
2129  if (!pnbytes)
2130  return ERROR_INT("&nbytes not defined", __func__, 1);
2131  if (!pw && !ph && !pminisblack)
2132  return ERROR_INT("no output data requested", __func__, 1);
2133  *pdata = NULL;
2134  *pnbytes = 0;
2135 
2136  if ((fpin = fopenReadStream(filein)) == NULL)
2137  return ERROR_INT("stream not opened to file", __func__, 1);
2138  istiff = fileFormatIsTiff(fpin);
2139  fclose(fpin);
2140  if (!istiff)
2141  return ERROR_INT("filein not tiff", __func__, 1);
2142 
2143  if ((inarray = l_binaryRead(filein, &fbytes)) == NULL)
2144  return ERROR_INT("inarray not made", __func__, 1);
2145 
2146  /* Get metadata about the image */
2147  if ((tif = openTiff(filein, "rb")) == NULL) {
2148  LEPT_FREE(inarray);
2149  return ERROR_INT("tif not open for read", __func__, 1);
2150  }
2151  TIFFGetField(tif, TIFFTAG_COMPRESSION, &comptype);
2152  if (comptype != COMPRESSION_CCITTFAX4) {
2153  LEPT_FREE(inarray);
2154  TIFFClose(tif);
2155  return ERROR_INT("filein is not g4 compressed", __func__, 1);
2156  }
2157 
2158  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
2159  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
2160  TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
2161  if (h != rowsperstrip)
2162  L_WARNING("more than 1 strip\n", __func__);
2163  /* From the standard:
2164  TIFFTAG_PHOTOMETRIC = 0 (false) --> min value is white.
2165  TIFFTAG_PHOTOMETRIC = 1 (true) --> min value is black.
2166  Most 1 bpp tiffs have the tag value 0 (black is 1),
2167  because there are fewer black pixels than white pixels,
2168  so it makes sense to encode runs of black pixels. */
2169  TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &minisblack);
2170 /* TIFFPrintDirectory(tif, stderr, 0); */
2171  TIFFClose(tif);
2172  if (pw) *pw = (l_int32)w;
2173  if (ph) *ph = (l_int32)h;
2174  if (pminisblack) *pminisblack = (l_int32)minisblack;
2175 
2176  /* The header has 8 bytes: the first 2 are the magic number,
2177  * the next 2 are the version, and the last 4 are the
2178  * offset to the first directory. That's what we want here.
2179  * We have to test the byte order before decoding 4 bytes! */
2180  if (inarray[0] == 0x4d) { /* big-endian */
2181  diroff = (inarray[4] << 24) | (inarray[5] << 16) |
2182  (inarray[6] << 8) | inarray[7];
2183  } else { /* inarray[0] == 0x49 : little-endian */
2184  diroff = (inarray[7] << 24) | (inarray[6] << 16) |
2185  (inarray[5] << 8) | inarray[4];
2186  }
2187 /* lept_stderr(" diroff = %d, %x\n", diroff, diroff); */
2188 
2189  /* Extract the ccittg4 encoded data from the tiff file.
2190  * We skip the 8 byte header and take nbytes of data,
2191  * up to the beginning of the directory (at diroff) */
2192  nbytes = diroff - 8;
2193  if (nbytes > MaxNumTiffBytes) {
2194  LEPT_FREE(inarray);
2195  L_ERROR("requesting %zu bytes > %zu\n", __func__,
2196  nbytes, MaxNumTiffBytes);
2197  return 1;
2198  }
2199  *pnbytes = nbytes;
2200  if ((data = (l_uint8 *)LEPT_CALLOC(nbytes, sizeof(l_uint8))) == NULL) {
2201  LEPT_FREE(inarray);
2202  return ERROR_INT("data not allocated", __func__, 1);
2203  }
2204  *pdata = data;
2205  memcpy(data, inarray + 8, nbytes);
2206  LEPT_FREE(inarray);
2207 
2208  return 0;
2209 }
2210 
2211 
2212 /*--------------------------------------------------------------*
2213  * Open tiff stream from file stream *
2214  *--------------------------------------------------------------*/
2235 static TIFF *
2236 fopenTiff(FILE *fp,
2237  const char *modestring)
2238 {
2239  if (!fp)
2240  return (TIFF *)ERROR_PTR("stream not opened", __func__, NULL);
2241  if (!modestring)
2242  return (TIFF *)ERROR_PTR("modestring not defined", __func__, NULL);
2243 
2244  TIFFSetWarningHandler(NULL); /* disable warnings */
2245  TIFFSetErrorHandler(NULL); /* disable error messages */
2246 
2247  fseek(fp, 0, SEEK_SET);
2248  return TIFFClientOpen("TIFFstream", modestring, (thandle_t)fp,
2249  lept_read_proc, lept_write_proc, lept_seek_proc,
2250  lept_close_proc, lept_size_proc, NULL, NULL);
2251 }
2252 
2253 
2254 /*--------------------------------------------------------------*
2255  * Wrapper for TIFFOpen *
2256  *--------------------------------------------------------------*/
2269 static TIFF *
2270 openTiff(const char *filename,
2271  const char *modestring)
2272 {
2273 char *fname;
2274 TIFF *tif;
2275 
2276  if (!filename)
2277  return (TIFF *)ERROR_PTR("filename not defined", __func__, NULL);
2278  if (!modestring)
2279  return (TIFF *)ERROR_PTR("modestring not defined", __func__, NULL);
2280 
2281  TIFFSetWarningHandler(NULL); /* disable warnings */
2282  TIFFSetErrorHandler(NULL); /* disable error messages */
2283 
2284  fname = genPathname(filename, NULL);
2285  tif = TIFFOpen(fname, modestring);
2286  LEPT_FREE(fname);
2287  return tif;
2288 }
2289 
2290 
2291 /*----------------------------------------------------------------------*
2292  * Memory I/O: reading memory --> pix and writing pix --> memory *
2293  *----------------------------------------------------------------------*/
2294 /* It would be nice to use open_memstream() and fmemopen()
2295  * for writing and reading to memory, rsp. These functions manage
2296  * memory for writes and reads that use a file streams interface.
2297  * Unfortunately, the tiff library only has an interface for reading
2298  * and writing to file descriptors, not to file streams. The tiff
2299  * library procedure is to open a "tiff stream" and read/write to it.
2300  * The library provides a client interface for managing the I/O
2301  * from memory, which requires seven callbacks. See the TIFFClientOpen
2302  * man page for callback signatures. Adam Langley provided the code
2303  * to do this. */
2304 
2326 {
2327  l_uint8 *buffer; /* expands to hold data when written to; */
2328  /* fixed size when read from. */
2329  size_t bufsize; /* current size allocated when written to; */
2330  /* fixed size of input data when read from. */
2331  size_t offset; /* byte offset from beginning of buffer. */
2332  size_t hw; /* high-water mark; max bytes in buffer. */
2333  l_uint8 **poutdata; /* input param for writing; data goes here. */
2334  size_t *poutsize; /* input param for writing; data size goes here. */
2335 };
2336 typedef struct L_Memstream L_MEMSTREAM;
2337 
2338 
2339  /* These are static functions for memory I/O */
2340 static L_MEMSTREAM *memstreamCreateForRead(l_uint8 *indata, size_t pinsize);
2341 static L_MEMSTREAM *memstreamCreateForWrite(l_uint8 **poutdata,
2342  size_t *poutsize);
2343 static tsize_t tiffReadCallback(thandle_t handle, tdata_t data, tsize_t length);
2344 static tsize_t tiffWriteCallback(thandle_t handle, tdata_t data,
2345  tsize_t length);
2346 static toff_t tiffSeekCallback(thandle_t handle, toff_t offset, l_int32 whence);
2347 static l_int32 tiffCloseCallback(thandle_t handle);
2348 static toff_t tiffSizeCallback(thandle_t handle);
2349 static l_int32 tiffMapCallback(thandle_t handle, tdata_t *data, toff_t *length);
2350 static void tiffUnmapCallback(thandle_t handle, tdata_t data, toff_t length);
2351 
2352 
2353 static L_MEMSTREAM *
2354 memstreamCreateForRead(l_uint8 *indata,
2355  size_t insize)
2356 {
2357 L_MEMSTREAM *mstream;
2358 
2359  mstream = (L_MEMSTREAM *)LEPT_CALLOC(1, sizeof(L_MEMSTREAM));
2360  mstream->buffer = indata; /* handle to input data array */
2361  mstream->bufsize = insize; /* amount of input data */
2362  mstream->hw = insize; /* high-water mark fixed at input data size */
2363  mstream->offset = 0; /* offset always starts at 0 */
2364  return mstream;
2365 }
2366 
2367 
2368 static L_MEMSTREAM *
2369 memstreamCreateForWrite(l_uint8 **poutdata,
2370  size_t *poutsize)
2371 {
2372 L_MEMSTREAM *mstream;
2373 
2374  mstream = (L_MEMSTREAM *)LEPT_CALLOC(1, sizeof(L_MEMSTREAM));
2375  mstream->buffer = (l_uint8 *)LEPT_CALLOC(8 * 1024, 1);
2376  mstream->bufsize = 8 * 1024;
2377  mstream->poutdata = poutdata; /* used only at end of write */
2378  mstream->poutsize = poutsize; /* ditto */
2379  mstream->hw = mstream->offset = 0;
2380  return mstream;
2381 }
2382 
2383 
2384 static tsize_t
2385 tiffReadCallback(thandle_t handle,
2386  tdata_t data,
2387  tsize_t length)
2388 {
2389 L_MEMSTREAM *mstream;
2390 size_t amount;
2391 
2392  mstream = (L_MEMSTREAM *)handle;
2393  amount = L_MIN((size_t)length, mstream->hw - mstream->offset);
2394 
2395  /* Fuzzed files can create this condition! */
2396  if (mstream->offset + amount < amount || /* overflow */
2397  mstream->offset + amount > mstream->hw) {
2398  lept_stderr("Bad file: amount too big: %zu\n", amount);
2399  return 0;
2400  }
2401 
2402  memcpy(data, mstream->buffer + mstream->offset, amount);
2403  mstream->offset += amount;
2404  return amount;
2405 }
2406 
2407 
2408 static tsize_t
2409 tiffWriteCallback(thandle_t handle,
2410  tdata_t data,
2411  tsize_t length)
2412 {
2413 L_MEMSTREAM *mstream;
2414 size_t newsize;
2415 
2416  /* reallocNew() uses calloc to initialize the array.
2417  * If malloc is used instead, for some of the encoding methods,
2418  * not all the data in 'bufsize' bytes in the buffer will
2419  * have been initialized by the end of the compression. */
2420  mstream = (L_MEMSTREAM *)handle;
2421  if (mstream->offset + length > mstream->bufsize) {
2422  newsize = 2 * (mstream->offset + length);
2423  mstream->buffer = (l_uint8 *)reallocNew((void **)&mstream->buffer,
2424  mstream->hw, newsize);
2425  mstream->bufsize = newsize;
2426  }
2427 
2428  memcpy(mstream->buffer + mstream->offset, data, length);
2429  mstream->offset += length;
2430  mstream->hw = L_MAX(mstream->offset, mstream->hw);
2431  return length;
2432 }
2433 
2434 
2435 static toff_t
2436 tiffSeekCallback(thandle_t handle,
2437  toff_t offset,
2438  l_int32 whence)
2439 {
2440 L_MEMSTREAM *mstream;
2441 
2442  mstream = (L_MEMSTREAM *)handle;
2443  switch (whence) {
2444  case SEEK_SET:
2445 /* lept_stderr("seek_set: offset = %d\n", offset); */
2446  if((size_t)offset != offset) { /* size_t overflow on uint32 */
2447  return (toff_t)ERROR_INT("too large offset value", __func__, 1);
2448  }
2449  mstream->offset = offset;
2450  break;
2451  case SEEK_CUR:
2452 /* lept_stderr("seek_cur: offset = %d\n", offset); */
2453  mstream->offset += offset;
2454  break;
2455  case SEEK_END:
2456 /* lept_stderr("seek end: hw = %d, offset = %d\n",
2457  mstream->hw, offset); */
2458  mstream->offset = mstream->hw - offset; /* offset >= 0 */
2459  break;
2460  default:
2461  return (toff_t)ERROR_INT("bad whence value", __func__,
2462  mstream->offset);
2463  }
2464 
2465  return mstream->offset;
2466 }
2467 
2468 
2469 static l_int32
2470 tiffCloseCallback(thandle_t handle)
2471 {
2472 L_MEMSTREAM *mstream;
2473 
2474  mstream = (L_MEMSTREAM *)handle;
2475  if (mstream->poutdata) { /* writing: save the output data */
2476  *mstream->poutdata = mstream->buffer;
2477  *mstream->poutsize = mstream->hw;
2478  }
2479  LEPT_FREE(mstream); /* never free the buffer! */
2480  return 0;
2481 }
2482 
2483 
2484 static toff_t
2485 tiffSizeCallback(thandle_t handle)
2486 {
2487 L_MEMSTREAM *mstream;
2488 
2489  mstream = (L_MEMSTREAM *)handle;
2490  return mstream->hw;
2491 }
2492 
2493 
2494 static l_int32
2495 tiffMapCallback(thandle_t handle,
2496  tdata_t *data,
2497  toff_t *length)
2498 {
2499 L_MEMSTREAM *mstream;
2500 
2501  mstream = (L_MEMSTREAM *)handle;
2502  *data = mstream->buffer;
2503  *length = mstream->hw;
2504  return 0;
2505 }
2506 
2507 
2508 static void
2509 tiffUnmapCallback(thandle_t handle,
2510  tdata_t data,
2511  toff_t length)
2512 {
2513  return;
2514 }
2515 
2516 
2537 static TIFF *
2538 fopenTiffMemstream(const char *filename,
2539  const char *operation,
2540  l_uint8 **pdata,
2541  size_t *pdatasize)
2542 {
2543 L_MEMSTREAM *mstream;
2544 TIFF *tif;
2545 
2546  if (!filename)
2547  return (TIFF *)ERROR_PTR("filename not defined", __func__, NULL);
2548  if (!operation)
2549  return (TIFF *)ERROR_PTR("operation not defined", __func__, NULL);
2550  if (!pdata)
2551  return (TIFF *)ERROR_PTR("&data not defined", __func__, NULL);
2552  if (!pdatasize)
2553  return (TIFF *)ERROR_PTR("&datasize not defined", __func__, NULL);
2554  if (strcmp(operation, "r") && strcmp(operation, "w"))
2555  return (TIFF *)ERROR_PTR("op not 'r' or 'w'", __func__, NULL);
2556 
2557  if (!strcmp(operation, "r"))
2558  mstream = memstreamCreateForRead(*pdata, *pdatasize);
2559  else
2560  mstream = memstreamCreateForWrite(pdata, pdatasize);
2561  if (!mstream)
2562  return (TIFF *)ERROR_PTR("mstream not made", __func__, NULL);
2563 
2564  TIFFSetWarningHandler(NULL); /* disable warnings */
2565  TIFFSetErrorHandler(NULL); /* disable error messages */
2566 
2567  tif = TIFFClientOpen(filename, operation, (thandle_t)mstream,
2568  tiffReadCallback, tiffWriteCallback,
2569  tiffSeekCallback, tiffCloseCallback,
2570  tiffSizeCallback, tiffMapCallback,
2571  tiffUnmapCallback);
2572  if (!tif)
2573  LEPT_FREE(mstream);
2574  return tif;
2575 }
2576 
2577 
2598 PIX *
2599 pixReadMemTiff(const l_uint8 *cdata,
2600  size_t size,
2601  l_int32 n)
2602 {
2603 l_uint8 *data;
2604 l_int32 i;
2605 PIX *pix;
2606 TIFF *tif;
2607 
2608  if (!cdata)
2609  return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL);
2610 
2611  data = (l_uint8 *)cdata; /* we're really not going to change this */
2612  if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL)
2613  return (PIX *)ERROR_PTR("tiff stream not opened", __func__, NULL);
2614 
2615  pix = NULL;
2616  for (i = 0; ; i++) {
2617  if (i == n) {
2618  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
2619  TIFFClose(tif);
2620  return NULL;
2621  }
2622  pixSetInputFormat(pix, IFF_TIFF);
2623  break;
2624  }
2625  if (TIFFReadDirectory(tif) == 0)
2626  break;
2627  if (i == ManyPagesInTiffFile + 1) {
2628  L_WARNING("big file: more than %d pages\n", __func__,
2629  ManyPagesInTiffFile);
2630  }
2631  }
2632 
2633  TIFFClose(tif);
2634  return pix;
2635 }
2636 
2637 
2661 PIX *
2662 pixReadMemFromMultipageTiff(const l_uint8 *cdata,
2663  size_t size,
2664  size_t *poffset)
2665 {
2666 l_uint8 *data;
2667 l_int32 retval;
2668 size_t offset;
2669 PIX *pix;
2670 TIFF *tif;
2671 
2672  if (!cdata)
2673  return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL);
2674  if (!poffset)
2675  return (PIX *)ERROR_PTR("&offset not defined", __func__, NULL);
2676 
2677  data = (l_uint8 *)cdata; /* we're really not going to change this */
2678  if ((tif = fopenTiffMemstream("tifferror", "r", &data, &size)) == NULL)
2679  return (PIX *)ERROR_PTR("tiff stream not opened", __func__, NULL);
2680 
2681  /* Set ptrs in the TIFF to the beginning of the image */
2682  offset = *poffset;
2683  retval = (offset == 0) ? TIFFSetDirectory(tif, 0)
2684  : TIFFSetSubDirectory(tif, offset);
2685  if (retval == 0) {
2686  TIFFClose(tif);
2687  return NULL;
2688  }
2689 
2690  if ((pix = pixReadFromTiffStream(tif)) == NULL) {
2691  TIFFClose(tif);
2692  return NULL;
2693  }
2694 
2695  /* Advance to the next image and return the new offset */
2696  TIFFReadDirectory(tif);
2697  *poffset = TIFFCurrentDirOffset(tif);
2698  TIFFClose(tif);
2699  return pix;
2700 }
2701 
2702 
2715 PIXA *
2716 pixaReadMemMultipageTiff(const l_uint8 *data,
2717  size_t size)
2718 {
2719 size_t offset;
2720 PIX *pix;
2721 PIXA *pixa;
2722 
2723  if (!data)
2724  return (PIXA *)ERROR_PTR("data not defined", __func__, NULL);
2725 
2726  offset = 0;
2727  pixa = pixaCreate(0);
2728  do {
2729  pix = pixReadMemFromMultipageTiff(data, size, &offset);
2730  pixaAddPix(pixa, pix, L_INSERT);
2731  } while (offset != 0);
2732  return pixa;
2733 }
2734 
2735 
2753 l_ok
2755  size_t *psize,
2756  PIXA *pixa)
2757 {
2758 const char *modestr;
2759 l_int32 i, n;
2760 FILE *fp;
2761 PIX *pix1;
2762 
2763  if (pdata) *pdata = NULL;
2764  if (!pdata)
2765  return ERROR_INT("pdata not defined", __func__, 1);
2766  if (!pixa)
2767  return ERROR_INT("pixa not defined", __func__, 1);
2768 
2769 #ifdef _WIN32
2770  if ((fp = fopenWriteWinTempfile()) == NULL)
2771  return ERROR_INT("tmpfile stream not opened", __func__, 1);
2772 #else
2773  if ((fp = tmpfile()) == NULL)
2774  return ERROR_INT("tmpfile stream not opened", __func__, 1);
2775 #endif /* _WIN32 */
2776 
2777  n = pixaGetCount(pixa);
2778  for (i = 0; i < n; i++) {
2779  modestr = (i == 0) ? "w" : "a";
2780  pix1 = pixaGetPix(pixa, i, L_CLONE);
2781  if (pixGetDepth(pix1) == 1)
2782  pixWriteStreamTiffWA(fp, pix1, IFF_TIFF_G4, modestr);
2783  else
2784  pixWriteStreamTiffWA(fp, pix1, IFF_TIFF_ZIP, modestr);
2785  pixDestroy(&pix1);
2786  }
2787 
2788  rewind(fp);
2789  *pdata = l_binaryReadStream(fp, psize);
2790  fclose(fp);
2791  return 0;
2792 }
2793 
2794 
2810 l_ok
2811 pixWriteMemTiff(l_uint8 **pdata,
2812  size_t *psize,
2813  PIX *pix,
2814  l_int32 comptype)
2815 {
2816  return pixWriteMemTiffCustom(pdata, psize, pix, comptype,
2817  NULL, NULL, NULL, NULL);
2818 }
2819 
2820 
2841 l_ok
2842 pixWriteMemTiffCustom(l_uint8 **pdata,
2843  size_t *psize,
2844  PIX *pix,
2845  l_int32 comptype,
2846  NUMA *natags,
2847  SARRAY *savals,
2848  SARRAY *satypes,
2849  NUMA *nasizes)
2850 {
2851 l_int32 ret;
2852 TIFF *tif;
2853 
2854  if (!pdata)
2855  return ERROR_INT("&data not defined", __func__, 1);
2856  if (!psize)
2857  return ERROR_INT("&size not defined", __func__, 1);
2858  if (!pix)
2859  return ERROR_INT("&pix not defined", __func__, 1);
2860  if (pixGetDepth(pix) != 1 && comptype != IFF_TIFF &&
2861  comptype != IFF_TIFF_LZW && comptype != IFF_TIFF_ZIP &&
2862  comptype != IFF_TIFF_JPEG) {
2863  L_WARNING("invalid compression type for bpp > 1\n", __func__);
2864  comptype = IFF_TIFF_ZIP;
2865  }
2866 
2867  if ((tif = fopenTiffMemstream("tifferror", "w", pdata, psize)) == NULL)
2868  return ERROR_INT("tiff stream not opened", __func__, 1);
2869  ret = pixWriteToTiffStream(tif, pix, comptype, natags, savals,
2870  satypes, nasizes);
2871 
2872  TIFFClose(tif);
2873  return ret;
2874 }
2875 
2876 /* --------------------------------------------*/
2877 #endif /* HAVE_LIBTIFF */
2878 /* --------------------------------------------*/
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:683
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:126
l_ok pixcmapToArrays(const PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap)
pixcmapToArrays()
Definition: colormap.c:1981
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:403
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:630
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:720
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1582
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1074
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1408
l_ok pixSetText(PIX *pix, const char *textstring)
pixSetText()
Definition: pix1.c:1430
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixEndianByteSwapNew(PIX *pixs)
pixEndianByteSwapNew()
Definition: pix2.c:2979
l_ok pixEndianTwoByteSwap(PIX *pixs)
pixEndianTwoByteSwap()
Definition: pix2.c:3200
l_ok pixSetPadBits(PIX *pix, l_int32 val)
pixSetPadBits()
Definition: pix2.c:1346
l_ok composeRGBAPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_int32 aval, l_uint32 *ppixel)
composeRGBAPixel()
Definition: pix2.c:2758
PIX * pixEndianTwoByteSwapNew(PIX *pixs)
pixEndianTwoByteSwapNew()
Definition: pix2.c:3148
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2728
l_ok pixEndianByteSwap(PIX *pixs)
pixEndianByteSwap()
Definition: pix2.c:3041
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1481
@ COLOR_BLUE
Definition: pix.h:330
@ COLOR_RED
Definition: pix.h:328
@ L_ALPHA_CHANNEL
Definition: pix.h:331
@ COLOR_GREEN
Definition: pix.h:329
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:384
@ L_CLONE
Definition: pix.h:506
@ L_NOCOPY
Definition: pix.h:503
@ L_INSERT
Definition: pix.h:504
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:493
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:629
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:647
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:324
l_ok findFileFormat(const char *filename, l_int32 *pformat)
findFileFormat()
Definition: readfile.c:570
l_ok findFileFormatStream(FILE *fp, l_int32 *pformat)
findFileFormatStream()
Definition: readfile.c:603
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:189
l_int32 fileFormatIsTiff(FILE *fp)
fileFormatIsTiff()
Definition: readfile.c:780
PIX * pixRotate90(PIX *pixs, l_int32 direction)
pixRotate90()
Definition: rotateorth.c:162
PIX * pixFlipLR(PIX *pixd, PIX *pixs)
pixFlipLR()
Definition: rotateorth.c:421
PIX * pixFlipTB(PIX *pixd, PIX *pixs)
pixFlipTB()
Definition: rotateorth.c:597
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:673
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:617
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:353
SARRAY * getSortedPathnamesInDirectory(const char *dirname, const char *substr, l_int32 first, l_int32 nfiles)
getSortedPathnamesInDirectory()
Definition: sarray1.c:1739
Memory stream buffer used with TIFFClientOpen()
Definition: tiffio.c:2326
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:1957
static TIFF * fopenTiffMemstream(const char *filename, const char *operation, l_uint8 **pdata, size_t *pdatasize)
fopenTiffMemstream()
Definition: tiffio.c:2538
l_ok pixaWriteMemMultipageTiff(l_uint8 **pdata, size_t *psize, PIXA *pixa)
pixaWriteMemMultipageTiff()
Definition: tiffio.c:2754
l_ok fprintTiffInfo(FILE *fpout, const char *tiffile)
fprintTiffInfo()
Definition: tiffio.c:1602
l_ok pixWriteStreamTiffWA(FILE *fp, PIX *pix, l_int32 comptype, const char *modestr)
pixWriteStreamTiffWA()
Definition: tiffio.c:950
static TIFF * openTiff(const char *filename, const char *modestring)
openTiff()
Definition: tiffio.c:2270
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:1777
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:1898
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:2842
l_ok findTiffCompression(FILE *fp, l_int32 *pcomptype)
findTiffCompression()
Definition: tiffio.c:2025
PIX * pixReadMemFromMultipageTiff(const l_uint8 *cdata, size_t size, size_t *poffset)
pixReadMemFromMultipageTiff()
Definition: tiffio.c:2662
static l_int32 writeCustomTiffTags(TIFF *tif, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes)
writeCustomTiffTags()
Definition: tiffio.c:1234
l_ok getTiffResolution(FILE *fp, l_int32 *pxres, l_int32 *pyres)
getTiffResolution()
Definition: tiffio.c:1679
PIX * pixReadMemTiff(const l_uint8 *cdata, size_t size, l_int32 n)
pixReadMemTiff()
Definition: tiffio.c:2599
static PIX * pixReadFromTiffStream(TIFF *tif)
pixReadFromTiffStream()
Definition: tiffio.c:497
l_ok pixWriteTiff(const char *filename, PIX *pix, l_int32 comptype, const char *modestr)
pixWriteTiff()
Definition: tiffio.c:811
l_ok writeMultipageTiff(const char *dirin, const char *substr, const char *fileout)
writeMultipageTiff()
Definition: tiffio.c:1517
PIX * pixReadFromMultipageTiff(const char *fname, size_t *poffset)
pixReadFromMultipageTiff()
Definition: tiffio.c:1360
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:868
static l_int32 pixWriteToTiffStream(TIFF *tif, PIX *pix, l_int32 comptype, NUMA *natags, SARRAY *savals, SARRAY *satypes, NUMA *nasizes)
pixWriteToTiffStream()
Definition: tiffio.c:1023
PIX * pixReadStreamTiff(FILE *fp, l_int32 n)
pixReadStreamTiff()
Definition: tiffio.c:429
l_ok pixWriteStreamTiff(FILE *fp, PIX *pix, l_int32 comptype)
pixWriteStreamTiff()
Definition: tiffio.c:925
l_ok writeMultipageTiffSA(SARRAY *sa, const char *fileout)
writeMultipageTiffSA()
Definition: tiffio.c:1551
l_ok pixWriteMemTiff(l_uint8 **pdata, size_t *psize, PIX *pix, l_int32 comptype)
pixWriteMemTiff()
Definition: tiffio.c:2811
static TIFF * fopenTiff(FILE *fp, const char *modestring)
fopenTiff()
Definition: tiffio.c:2236
l_ok pixaWriteMultipageTiff(const char *fname, PIXA *pixa)
pixaWriteMultipageTiff()
Definition: tiffio.c:1465
PIX * pixReadTiff(const char *filename, l_int32 n)
pixReadTiff()
Definition: tiffio.c:394
static l_int32 getTiffCompressedFormat(l_uint16 tiffcomp)
getTiffCompressedFormat()
Definition: tiffio.c:2061
l_ok tiffGetCount(FILE *fp, l_int32 *pn)
tiffGetCount()
Definition: tiffio.c:1633
PIXA * pixaReadMemMultipageTiff(const l_uint8 *data, size_t size)
pixaReadMemMultipageTiff()
Definition: tiffio.c:2716
static l_int32 getTiffStreamResolution(TIFF *tif, l_int32 *pxres, l_int32 *pyres)
getTiffStreamResolution()
Definition: tiffio.c:1713
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:1831
PIXA * pixaReadMultipageTiff(const char *filename)
pixaReadMultipageTiff()
Definition: tiffio.c:1407
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:2111
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
l_uint8 * l_binaryReadStream(FILE *fp, size_t *pnbytes)
l_binaryReadStream()
Definition: utils2.c:1358
char * genPathname(const char *dir, const char *fname)
genPathname()
Definition: utils2.c:3068
void * reallocNew(void **pindata, size_t oldsize, size_t newsize)
reallocNew()
Definition: utils2.c:1262
FILE * fopenWriteWinTempfile(void)
fopenWriteWinTempfile()
Definition: utils2.c:1981
FILE * fopenReadStream(const char *filename)
fopenReadStream()
Definition: utils2.c:1864
l_uint8 * l_binaryRead(const char *filename, size_t *pnbytes)
l_binaryRead()
Definition: utils2.c:1310