summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'leptonica/src/pixcomp.c')
-rw-r--r--leptonica/src/pixcomp.c2481
1 files changed, 2481 insertions, 0 deletions
diff --git a/leptonica/src/pixcomp.c b/leptonica/src/pixcomp.c
new file mode 100644
index 00000000..568a280c
--- /dev/null
+++ b/leptonica/src/pixcomp.c
@@ -0,0 +1,2481 @@
+/*====================================================================*
+ - Copyright (C) 2001 Leptonica. All rights reserved.
+ -
+ - Redistribution and use in source and binary forms, with or without
+ - modification, are permitted provided that the following conditions
+ - are met:
+ - 1. Redistributions of source code must retain the above copyright
+ - notice, this list of conditions and the following disclaimer.
+ - 2. Redistributions in binary form must reproduce the above
+ - copyright notice, this list of conditions and the following
+ - disclaimer in the documentation and/or other materials
+ - provided with the distribution.
+ -
+ - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
+ - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *====================================================================*/
+
+/*!
+ * \file pixcomp.c
+ * <pre>
+ *
+ * Pixcomp creation and destruction
+ * PIXC *pixcompCreateFromPix()
+ * PIXC *pixcompCreateFromString()
+ * PIXC *pixcompCreateFromFile()
+ * void pixcompDestroy()
+ * PIXC *pixcompCopy()
+
+ * Pixcomp accessors
+ * l_int32 pixcompGetDimensions()
+ * l_int32 pixcompGetParameters()
+ *
+ * Pixcomp compression selection
+ * l_int32 pixcompDetermineFormat()
+ *
+ * Pixcomp conversion to Pix
+ * PIX *pixCreateFromPixcomp()
+ *
+ * Pixacomp creation and destruction
+ * PIXAC *pixacompCreate()
+ * PIXAC *pixacompCreateWithInit()
+ * PIXAC *pixacompCreateFromPixa()
+ * PIXAC *pixacompCreateFromFiles()
+ * PIXAC *pixacompCreateFromSA()
+ * void pixacompDestroy()
+ *
+ * Pixacomp addition/replacement
+ * l_int32 pixacompAddPix()
+ * l_int32 pixacompAddPixcomp()
+ * static l_int32 pixacompExtendArray()
+ * l_int32 pixacompReplacePix()
+ * l_int32 pixacompReplacePixcomp()
+ * l_int32 pixacompAddBox()
+ *
+ * Pixacomp accessors
+ * l_int32 pixacompGetCount()
+ * PIXC *pixacompGetPixcomp()
+ * PIX *pixacompGetPix()
+ * l_int32 pixacompGetPixDimensions()
+ * BOXA *pixacompGetBoxa()
+ * l_int32 pixacompGetBoxaCount()
+ * BOX *pixacompGetBox()
+ * l_int32 pixacompGetBoxGeometry()
+ * l_int32 pixacompGetOffset()
+ * l_int32 pixacompSetOffset()
+ *
+ * Pixacomp conversion to Pixa
+ * PIXA *pixaCreateFromPixacomp()
+ *
+ * Combining pixacomp
+ * l_int32 pixacompJoin()
+ * PIXAC *pixacompInterleave()
+ *
+ * Pixacomp serialized I/O
+ * PIXAC *pixacompRead()
+ * PIXAC *pixacompReadStream()
+ * PIXAC *pixacompReadMem()
+ * l_int32 pixacompWrite()
+ * l_int32 pixacompWriteStream()
+ * l_int32 pixacompWriteMem()
+ *
+ * Conversion to pdf
+ * l_int32 pixacompConvertToPdf()
+ * l_int32 pixacompConvertToPdfData()
+ * l_int32 pixacompFastConvertToPdfData()
+ *
+ * Output for debugging
+ * l_int32 pixacompWriteStreamInfo()
+ * l_int32 pixcompWriteStreamInfo()
+ * PIX *pixacompDisplayTiledAndScaled()
+ * l_int32 pixacompWriteFiles()
+ * l_int32 pixcompWriteFile()
+ *
+ * The Pixacomp is an array of Pixcomp, where each Pixcomp is a compressed
+ * string of the image. We don't use reference counting here.
+ * The basic application is to allow a large array of highly
+ * compressible images to reside in memory. We purposely don't
+ * reuse the Pixa for this, to avoid confusion and programming errors.
+ *
+ * Three compression formats are used: g4, png and jpeg.
+ * The compression type can be either specified or defaulted.
+ * If specified and it is not possible to compress (for example,
+ * you specify a jpeg on a 1 bpp image or one with a colormap),
+ * the compression type defaults to png. The jpeg compression quality
+ * can be specified using l_setJpegQuality(); otherwise the default is 75.
+ *
+ * The serialized version of the Pixacomp is similar to that for
+ * a Pixa, except that each Pixcomp can be compressed by one of
+ * tiffg4, png, or jpeg. Unlike serialization of the Pixa,
+ * serialization of the Pixacomp does not require any imaging
+ * libraries because it simply reads and writes the compressed data.
+ *
+ * There are two modes of use in accumulating images:
+ * (1) addition to the end of the array
+ * (2) random insertion (replacement) into the array
+ *
+ * In use, we assume that the array is fully populated up to the
+ * index value (n - 1), where n is the value of the pixcomp field n.
+ * Addition can only be made to the end of the fully populated array,
+ * at the index value n. Insertion can be made randomly, but again
+ * only within the array of pixcomps; i.e., within the set of
+ * indices {0 .... n-1}. The functions are pixacompReplacePix()
+ * and pixacompReplacePixcomp(), and they destroy the existing pixcomp.
+ *
+ * For addition to the end of the array, initialize the pixacomp with
+ * pixacompCreate(), which generates an empty array of pixcomps ptrs.
+ * For random insertion and replacement of pixcomp into a pixacomp,
+ * initialize a fully populated array using pixacompCreateWithInit().
+ *
+ * The offset field allows you to use an offset-based index to
+ * access the 0-based ptr array in the pixacomp. This would typically
+ * be used to map the pixacomp array index to a page number, or v.v.
+ * By default, the offset is 0. For example, suppose you have 50 images,
+ * corresponding to page numbers 10 - 59. Then you could use
+ * pixac = pixacompCreateWithInit(50, 10, ...);
+ * This would allocate an array of 50 pixcomps, but if you asked for
+ * the pix at index 10, using pixacompGetPix(pixac, 10), it would
+ * apply the offset internally, returning the pix at index 0 in the array.
+ * </pre>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config_auto.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include "allheaders.h"
+
+ /* Bounds on pixacomp array size */
+static const l_uint32 MaxPtrArraySize = 1000000;
+static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */
+
+ /* Bound on size for a compressed data string */
+static const size_t MaxDataSize = 1000000000; /* 1 GB */
+
+ /* These two globals are defined in writefile.c */
+extern l_int32 NumImageFileFormatExtensions;
+extern const char *ImageFileFormatExtensions[];
+
+ /* Static functions */
+static l_int32 pixacompExtendArray(PIXAC *pixac);
+static l_int32 pixcompFastConvertToPdfData(PIXC *pixc, const char *title,
+ l_uint8 **pdata, size_t *pnbytes);
+
+
+/*---------------------------------------------------------------------*
+ * Pixcomp creation and destruction *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixcompCreateFromPix()
+ *
+ * \param[in] pix
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \return pixc, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Use %comptype == IFF_DEFAULT to have the compression
+ * type automatically determined.
+ * (2) To compress jpeg with a quality other than the default (75), use
+ * l_jpegSetQuality()
+ * </pre>
+ */
+PIXC *
+pixcompCreateFromPix(PIX *pix,
+ l_int32 comptype)
+{
+size_t size;
+char *text;
+l_int32 ret, format;
+l_uint8 *data;
+PIXC *pixc;
+
+ PROCNAME("pixcompCreateFromPix");
+
+ if (!pix)
+ return (PIXC *)ERROR_PTR("pix not defined", procName, NULL);
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL);
+
+ pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
+ pixGetDimensions(pix, &pixc->w, &pixc->h, &pixc->d);
+ pixGetResolution(pix, &pixc->xres, &pixc->yres);
+ if (pixGetColormap(pix))
+ pixc->cmapflag = 1;
+ if ((text = pixGetText(pix)) != NULL)
+ pixc->text = stringNew(text);
+
+ pixcompDetermineFormat(comptype, pixc->d, pixc->cmapflag, &format);
+ pixc->comptype = format;
+ ret = pixWriteMem(&data, &size, pix, format);
+ if (ret) {
+ L_ERROR("write to memory failed\n", procName);
+ pixcompDestroy(&pixc);
+ return NULL;
+ }
+ pixc->data = data;
+ pixc->size = size;
+
+ return pixc;
+}
+
+
+/*!
+ * \brief pixcompCreateFromString()
+ *
+ * \param[in] data compressed string
+ * \param[in] size number of bytes
+ * \param[in] copyflag L_INSERT or L_COPY
+ * \return pixc, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) This works when the compressed string is png, jpeg or tiffg4.
+ * (2) The copyflag determines if the data in the new Pixcomp is
+ * a copy of the input data.
+ * </pre>
+ */
+PIXC *
+pixcompCreateFromString(l_uint8 *data,
+ size_t size,
+ l_int32 copyflag)
+{
+l_int32 format, w, h, d, bps, spp, iscmap;
+PIXC *pixc;
+
+ PROCNAME("pixcompCreateFromString");
+
+ if (!data)
+ return (PIXC *)ERROR_PTR("data not defined", procName, NULL);
+ if (copyflag != L_INSERT && copyflag != L_COPY)
+ return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL);
+
+ if (pixReadHeaderMem(data, size, &format, &w, &h, &bps, &spp, &iscmap) == 1)
+ return (PIXC *)ERROR_PTR("header data not read", procName, NULL);
+ pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
+ d = (spp == 3) ? 32 : bps * spp;
+ pixc->w = w;
+ pixc->h = h;
+ pixc->d = d;
+ pixc->comptype = format;
+ pixc->cmapflag = iscmap;
+ if (copyflag == L_INSERT)
+ pixc->data = data;
+ else
+ pixc->data = l_binaryCopy(data, size);
+ pixc->size = size;
+ return pixc;
+}
+
+
+/*!
+ * \brief pixcompCreateFromFile()
+ *
+ * \param[in] filename
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \return pixc, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Use %comptype == IFF_DEFAULT to have the compression
+ * type automatically determined.
+ * (2) If the comptype is invalid for this file, the default will
+ * be substituted.
+ * </pre>
+ */
+PIXC *
+pixcompCreateFromFile(const char *filename,
+ l_int32 comptype)
+{
+l_int32 format;
+size_t nbytes;
+l_uint8 *data;
+PIX *pix;
+PIXC *pixc;
+
+ PROCNAME("pixcompCreateFromFile");
+
+ if (!filename)
+ return (PIXC *)ERROR_PTR("filename not defined", procName, NULL);
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL);
+
+ findFileFormat(filename, &format);
+ if (format == IFF_UNKNOWN) {
+ L_ERROR("unreadable file: %s\n", procName, filename);
+ return NULL;
+ }
+
+ /* Can we accept the encoded file directly? Remember that
+ * png is the "universal" compression type, so if requested
+ * it takes precedence. Otherwise, if the file is already
+ * compressed in g4 or jpeg, just accept the string. */
+ if ((format == IFF_TIFF_G4 && comptype != IFF_PNG) ||
+ (format == IFF_JFIF_JPEG && comptype != IFF_PNG))
+ comptype = format;
+ if (comptype != IFF_DEFAULT && comptype == format) {
+ data = l_binaryRead(filename, &nbytes);
+ if ((pixc = pixcompCreateFromString(data, nbytes, L_INSERT)) == NULL) {
+ LEPT_FREE(data);
+ return (PIXC *)ERROR_PTR("pixc not made (string)", procName, NULL);
+ }
+ return pixc;
+ }
+
+ /* Need to recompress in the default format */
+ if ((pix = pixRead(filename)) == NULL)
+ return (PIXC *)ERROR_PTR("pix not read", procName, NULL);
+ if ((pixc = pixcompCreateFromPix(pix, comptype)) == NULL) {
+ pixDestroy(&pix);
+ return (PIXC *)ERROR_PTR("pixc not made", procName, NULL);
+ }
+ pixDestroy(&pix);
+ return pixc;
+}
+
+
+/*!
+ * \brief pixcompDestroy()
+ *
+ * \param[in,out] ppixc use ptr address so it will be nulled
+ * \return void
+ *
+ * <pre>
+ * Notes:
+ * (1) Always nulls the input ptr.
+ * </pre>
+ */
+void
+pixcompDestroy(PIXC **ppixc)
+{
+PIXC *pixc;
+
+ PROCNAME("pixcompDestroy");
+
+ if (!ppixc) {
+ L_WARNING("ptr address is null!\n", procName);
+ return;
+ }
+
+ if ((pixc = *ppixc) == NULL)
+ return;
+
+ LEPT_FREE(pixc->data);
+ if (pixc->text)
+ LEPT_FREE(pixc->text);
+ LEPT_FREE(pixc);
+ *ppixc = NULL;
+}
+
+
+/*!
+ * \brief pixcompCopy()
+ *
+ * \param[in] pixcs
+ * \return pixcd, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Limit the size of the compressed pix to 500 MB.
+ * </pre>
+ */
+PIXC *
+pixcompCopy(PIXC *pixcs)
+{
+size_t size;
+l_uint8 *datas, *datad;
+PIXC *pixcd;
+
+ PROCNAME("pixcompCopy");
+
+ if (!pixcs)
+ return (PIXC *)ERROR_PTR("pixcs not defined", procName, NULL);
+ size = pixcs->size;
+ if (size > MaxDataSize)
+ return (PIXC *)ERROR_PTR("size > 1 GB; too big", procName, NULL);
+
+ pixcd = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
+ pixcd->w = pixcs->w;
+ pixcd->h = pixcs->h;
+ pixcd->d = pixcs->d;
+ pixcd->xres = pixcs->xres;
+ pixcd->yres = pixcs->yres;
+ pixcd->comptype = pixcs->comptype;
+ if (pixcs->text != NULL)
+ pixcd->text = stringNew(pixcs->text);
+ pixcd->cmapflag = pixcs->cmapflag;
+
+ /* Copy image data */
+ datas = pixcs->data;
+ if ((datad = (l_uint8 *)LEPT_CALLOC(size, sizeof(l_int8))) == NULL) {
+ pixcompDestroy(&pixcd);
+ return (PIXC *)ERROR_PTR("pixcd not made", procName, NULL);
+ }
+ memcpy(datad, datas, size);
+ pixcd->data = datad;
+ pixcd->size = size;
+ return pixcd;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixcomp accessors *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixcompGetDimensions()
+ *
+ * \param[in] pixc
+ * \param[out] pw, ph, pd [optional]
+ * \return 0 if OK, 1 on error
+ */
+l_ok
+pixcompGetDimensions(PIXC *pixc,
+ l_int32 *pw,
+ l_int32 *ph,
+ l_int32 *pd)
+{
+ PROCNAME("pixcompGetDimensions");
+
+ if (!pixc)
+ return ERROR_INT("pixc not defined", procName, 1);
+ if (pw) *pw = pixc->w;
+ if (ph) *ph = pixc->h;
+ if (pd) *pd = pixc->d;
+ return 0;
+}
+
+
+/*!
+ * \brief pixcompGetParameters()
+ *
+ * \param[in] pixc
+ * \param[out] pxres, pyres, pcomptype, pcmapflag [optional]
+ * \return 0 if OK, 1 on error
+ */
+l_ok
+pixcompGetParameters(PIXC *pixc,
+ l_int32 *pxres,
+ l_int32 *pyres,
+ l_int32 *pcomptype,
+ l_int32 *pcmapflag)
+{
+ PROCNAME("pixcompGetParameters");
+
+ if (!pixc)
+ return ERROR_INT("pixc not defined", procName, 1);
+ if (pxres) *pxres = pixc->xres;
+ if (pyres) *pyres = pixc->yres;
+ if (pcomptype) *pcomptype = pixc->comptype;
+ if (pcmapflag) *pcmapflag = pixc->cmapflag;
+ return 0;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixcomp compression selection *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixcompDetermineFormat()
+ *
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \param[in] d pix depth
+ * \param[in] cmapflag 1 if pix to be compressed as a colormap; 0 otherwise
+ * \param[out] pformat IFF_TIFF, IFF_PNG or IFF_JFIF_JPEG
+ * \return 0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) This determines the best format for a pix, given both
+ * the request (%comptype) and the image characteristics.
+ * (2) If %comptype == IFF_DEFAULT, this does not necessarily result
+ * in png encoding. Instead, it returns one of the three formats
+ * that is both valid and most likely to give best compression.
+ * (3) If %d == 8 with no colormap and:
+ * * you wish to compress with png, use %comptype == IFF_PNG
+ * * you wish to compress with jpeg, use either
+ * %comptype == IFF_JFIF_JPEG or %comptype == IFF_DEFAULT.
+ * (4) If the pix cannot be compressed by the input value of
+ * %comptype, this selects IFF_PNG, which can compress all pix.
+ * </pre>
+ */
+l_ok
+pixcompDetermineFormat(l_int32 comptype,
+ l_int32 d,
+ l_int32 cmapflag,
+ l_int32 *pformat)
+{
+
+ PROCNAME("pixcompDetermineFormat");
+
+ if (!pformat)
+ return ERROR_INT("&format not defined", procName, 1);
+ *pformat = IFF_PNG; /* init value and default */
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return ERROR_INT("invalid comptype", procName, 1);
+
+ if (comptype == IFF_DEFAULT) {
+ if (d == 1)
+ *pformat = IFF_TIFF_G4;
+ else if (d == 16)
+ *pformat = IFF_PNG;
+ else if (d >= 8 && !cmapflag)
+ *pformat = IFF_JFIF_JPEG;
+ } else if (comptype == IFF_TIFF_G4 && d == 1) {
+ *pformat = IFF_TIFF_G4;
+ } else if (comptype == IFF_JFIF_JPEG && d >= 8 && !cmapflag) {
+ *pformat = IFF_JFIF_JPEG;
+ }
+
+ return 0;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixcomp conversion to Pix *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixCreateFromPixcomp()
+ *
+ * \param[in] pixc
+ * \return pix, or NULL on error
+ */
+PIX *
+pixCreateFromPixcomp(PIXC *pixc)
+{
+l_int32 w, h, d, cmapinpix, format;
+PIX *pix;
+
+ PROCNAME("pixCreateFromPixcomp");
+
+ if (!pixc)
+ return (PIX *)ERROR_PTR("pixc not defined", procName, NULL);
+
+ if ((pix = pixReadMem(pixc->data, pixc->size)) == NULL)
+ return (PIX *)ERROR_PTR("pix not read", procName, NULL);
+ pixSetResolution(pix, pixc->xres, pixc->yres);
+ if (pixc->text)
+ pixSetText(pix, pixc->text);
+
+ /* Check fields for consistency */
+ pixGetDimensions(pix, &w, &h, &d);
+ if (pixc->w != w) {
+ L_INFO("pix width %d != pixc width %d\n", procName, w, pixc->w);
+ L_ERROR("pix width %d != pixc width\n", procName, w);
+ }
+ if (pixc->h != h)
+ L_ERROR("pix height %d != pixc height\n", procName, h);
+ if (pixc->d != d) {
+ if (pixc->d == 16) /* we strip 16 --> 8 bpp by default */
+ L_WARNING("pix depth %d != pixc depth 16\n", procName, d);
+ else
+ L_ERROR("pix depth %d != pixc depth\n", procName, d);
+ }
+ cmapinpix = (pixGetColormap(pix) != NULL);
+ if ((cmapinpix && !pixc->cmapflag) || (!cmapinpix && pixc->cmapflag))
+ L_ERROR("pix cmap flag inconsistent\n", procName);
+ format = pixGetInputFormat(pix);
+ if (format != pixc->comptype) {
+ L_ERROR("pix comptype %d not equal to pixc comptype\n",
+ procName, format);
+ }
+
+ return pix;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixacomp creation and destruction *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixacompCreate()
+ *
+ * \param[in] n initial number of ptrs
+ * \return pixac, or NULL on error
+ */
+PIXAC *
+pixacompCreate(l_int32 n)
+{
+PIXAC *pixac;
+
+ PROCNAME("pixacompCreate");
+
+ if (n <= 0 || n > MaxPtrArraySize)
+ n = InitialPtrArraySize;
+
+ pixac = (PIXAC *)LEPT_CALLOC(1, sizeof(PIXAC));
+ pixac->n = 0;
+ pixac->nalloc = n;
+ pixac->offset = 0;
+ if ((pixac->pixc = (PIXC **)LEPT_CALLOC(n, sizeof(PIXC *))) == NULL) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("pixc ptrs not made", procName, NULL);
+ }
+ if ((pixac->boxa = boxaCreate(n)) == NULL) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
+ }
+
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompCreateWithInit()
+ *
+ * \param[in] n initial number of ptrs
+ * \param[in] offset difference: accessor index - pixacomp array index
+ * \param[in] pix [optional] initialize each ptr in pixacomp
+ * to this pix; can be NULL
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \return pixac, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Initializes a pixacomp to be fully populated with %pix,
+ * compressed using %comptype. If %pix == NULL, %comptype
+ * is ignored.
+ * (2) Typically, the array is initialized with a tiny pix.
+ * This is most easily done by setting %pix == NULL, causing
+ * initialization of each array element with a tiny placeholder
+ * pix (w = h = d = 1), using comptype = IFF_TIFF_G4 .
+ * (3) Example usage:
+ * // Generate pixacomp for pages 30 - 49. This has an array
+ * // size of 20 and the page number offset is 30.
+ * PixaComp *pixac = pixacompCreateWithInit(20, 30, NULL,
+ * IFF_TIFF_G4);
+ * // Now insert png-compressed images into the initialized array
+ * for (pageno = 30; pageno < 50; pageno++) {
+ * Pix *pixt = ... // derived from image[pageno]
+ * if (pixt)
+ * pixacompReplacePix(pixac, pageno, pixt, IFF_PNG);
+ * pixDestroy(&pixt);
+ * }
+ * The result is a pixac with 20 compressed strings, and with
+ * selected pixt replacing the placeholders.
+ * To extract the image for page 38, which is decompressed
+ * from element 8 in the array, use:
+ * pixt = pixacompGetPix(pixac, 38);
+ * </pre>
+ */
+PIXAC *
+pixacompCreateWithInit(l_int32 n,
+ l_int32 offset,
+ PIX *pix,
+ l_int32 comptype)
+{
+l_int32 i;
+PIX *pixt;
+PIXC *pixc;
+PIXAC *pixac;
+
+ PROCNAME("pixacompCreateWithInit");
+
+ if (n <= 0 || n > MaxPtrArraySize)
+ return (PIXAC *)ERROR_PTR("n out of valid bounds", procName, NULL);
+ if (pix) {
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
+ } else {
+ comptype = IFF_TIFF_G4;
+ }
+ if (offset < 0) {
+ L_WARNING("offset < 0; setting to 0\n", procName);
+ offset = 0;
+ }
+
+ if ((pixac = pixacompCreate(n)) == NULL)
+ return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
+ pixacompSetOffset(pixac, offset);
+ if (pix)
+ pixt = pixClone(pix);
+ else
+ pixt = pixCreate(1, 1, 1);
+ for (i = 0; i < n; i++) {
+ pixc = pixcompCreateFromPix(pixt, comptype);
+ pixacompAddPixcomp(pixac, pixc, L_INSERT);
+ }
+ pixDestroy(&pixt);
+
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompCreateFromPixa()
+ *
+ * \param[in] pixa
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) If %format == IFF_DEFAULT, the conversion format for each
+ * image is chosen automatically. Otherwise, we use the
+ * specified format unless it can't be done (e.g., jpeg
+ * for a 1, 2 or 4 bpp pix, or a pix with a colormap),
+ * in which case we use the default (assumed best) compression.
+ * (2) %accesstype is used to extract a boxa from %pixa.
+ * (3) To compress jpeg with a quality other than the default (75), use
+ * l_jpegSetQuality()
+ * </pre>
+ */
+PIXAC *
+pixacompCreateFromPixa(PIXA *pixa,
+ l_int32 comptype,
+ l_int32 accesstype)
+{
+l_int32 i, n;
+BOXA *boxa;
+PIX *pix;
+PIXAC *pixac;
+
+ PROCNAME("pixacompCreateFromPixa");
+
+ if (!pixa)
+ return (PIXAC *)ERROR_PTR("pixa not defined", procName, NULL);
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
+ if (accesstype != L_COPY && accesstype != L_CLONE &&
+ accesstype != L_COPY_CLONE)
+ return (PIXAC *)ERROR_PTR("invalid accesstype", procName, NULL);
+
+ n = pixaGetCount(pixa);
+ if ((pixac = pixacompCreate(n)) == NULL)
+ return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
+ for (i = 0; i < n; i++) {
+ pix = pixaGetPix(pixa, i, L_CLONE);
+ pixacompAddPix(pixac, pix, comptype);
+ pixDestroy(&pix);
+ }
+ if ((boxa = pixaGetBoxa(pixa, accesstype)) != NULL) {
+ boxaDestroy(&pixac->boxa);
+ pixac->boxa = boxa;
+ }
+
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompCreateFromFiles()
+ *
+ * \param[in] dirname
+ * \param[in] substr [optional] substring filter on filenames; can be null
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \return pixac, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) %dirname is the full path for the directory.
+ * (2) %substr is the part of the file name (excluding
+ * the directory) that is to be matched. All matching
+ * filenames are read into the Pixa. If substr is NULL,
+ * all filenames are read into the Pixa.
+ * (3) Use %comptype == IFF_DEFAULT to have the compression
+ * type automatically determined for each file.
+ * (4) If the comptype is invalid for a file, the default will
+ * be substituted.
+ * </pre>
+ */
+PIXAC *
+pixacompCreateFromFiles(const char *dirname,
+ const char *substr,
+ l_int32 comptype)
+{
+PIXAC *pixac;
+SARRAY *sa;
+
+ PROCNAME("pixacompCreateFromFiles");
+
+ if (!dirname)
+ return (PIXAC *)ERROR_PTR("dirname not defined", procName, NULL);
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
+
+ if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL)
+ return (PIXAC *)ERROR_PTR("sa not made", procName, NULL);
+ pixac = pixacompCreateFromSA(sa, comptype);
+ sarrayDestroy(&sa);
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompCreateFromSA()
+ *
+ * \param[in] sa full pathnames for all files
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \return pixac, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Use %comptype == IFF_DEFAULT to have the compression
+ * type automatically determined for each file.
+ * (2) If the comptype is invalid for a file, the default will
+ * be substituted.
+ * </pre>
+ */
+PIXAC *
+pixacompCreateFromSA(SARRAY *sa,
+ l_int32 comptype)
+{
+char *str;
+l_int32 i, n;
+PIXC *pixc;
+PIXAC *pixac;
+
+ PROCNAME("pixacompCreateFromSA");
+
+ if (!sa)
+ return (PIXAC *)ERROR_PTR("sarray not defined", procName, NULL);
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
+
+ n = sarrayGetCount(sa);
+ pixac = pixacompCreate(n);
+ for (i = 0; i < n; i++) {
+ str = sarrayGetString(sa, i, L_NOCOPY);
+ if ((pixc = pixcompCreateFromFile(str, comptype)) == NULL) {
+ L_ERROR("pixc not read from file: %s\n", procName, str);
+ continue;
+ }
+ pixacompAddPixcomp(pixac, pixc, L_INSERT);
+ }
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompDestroy()
+ *
+ * \param[in,out] ppixac use ptr address so it will be nulled
+ * \return void
+ *
+ * <pre>
+ * Notes:
+ * (1) Always nulls the input ptr.
+ * </pre>
+ */
+void
+pixacompDestroy(PIXAC **ppixac)
+{
+l_int32 i;
+PIXAC *pixac;
+
+ PROCNAME("pixacompDestroy");
+
+ if (ppixac == NULL) {
+ L_WARNING("ptr address is NULL!\n", procName);
+ return;
+ }
+
+ if ((pixac = *ppixac) == NULL)
+ return;
+
+ for (i = 0; i < pixac->n; i++)
+ pixcompDestroy(&pixac->pixc[i]);
+ LEPT_FREE(pixac->pixc);
+ boxaDestroy(&pixac->boxa);
+ LEPT_FREE(pixac);
+ *ppixac = NULL;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixacomp addition *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixacompAddPix()
+ *
+ * \param[in] pixac
+ * \param[in] pix to be added
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \return 0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The array is filled up to the (n-1)-th element, and this
+ * converts the input pix to a pixc and adds it at
+ * the n-th position.
+ * (2) The pixc produced from the pix is owned by the pixac.
+ * The input pix is not affected.
+ * </pre>
+ */
+l_ok
+pixacompAddPix(PIXAC *pixac,
+ PIX *pix,
+ l_int32 comptype)
+{
+l_int32 cmapflag, format;
+PIXC *pixc;
+
+ PROCNAME("pixacompAddPix");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ if (!pix)
+ return ERROR_INT("pix not defined", procName, 1);
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return ERROR_INT("invalid format", procName, 1);
+
+ cmapflag = pixGetColormap(pix) ? 1 : 0;
+ pixcompDetermineFormat(comptype, pixGetDepth(pix), cmapflag, &format);
+ if ((pixc = pixcompCreateFromPix(pix, format)) == NULL)
+ return ERROR_INT("pixc not made", procName, 1);
+ pixacompAddPixcomp(pixac, pixc, L_INSERT);
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompAddPixcomp()
+ *
+ * \param[in] pixac
+ * \param[in] pixc to be added by insertion
+ * \param[in] copyflag L_INSERT, L_COPY
+ * \return 0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Anything added to a pixac is owned by the pixac.
+ * So do not L_INSERT a pixc that is owned by another pixac,
+ * or destroy a pixc that has been L_INSERTed.
+ * </pre>
+ */
+l_ok
+pixacompAddPixcomp(PIXAC *pixac,
+ PIXC *pixc,
+ l_int32 copyflag)
+{
+l_int32 n;
+
+ PROCNAME("pixacompAddPixcomp");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ if (!pixc)
+ return ERROR_INT("pixc not defined", procName, 1);
+ if (copyflag != L_INSERT && copyflag != L_COPY)
+ return ERROR_INT("invalid copyflag", procName, 1);
+
+ n = pixac->n;
+ if (n >= pixac->nalloc) {
+ if (pixacompExtendArray(pixac))
+ return ERROR_INT("extension failed", procName, 1);
+ }
+
+ if (copyflag == L_INSERT)
+ pixac->pixc[n] = pixc;
+ else /* L_COPY */
+ pixac->pixc[n] = pixcompCopy(pixc);
+ pixac->n++;
+
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompExtendArray()
+ *
+ * \param[in] pixac
+ * \return 0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) We extend the boxa array simultaneously. This is
+ * necessary in case we are NOT adding boxes simultaneously
+ * with adding pixc. We always want the sizes of the
+ * pixac and boxa ptr arrays to be equal.
+ * (2) The max number of pixcomp ptrs is 1M.
+ * </pre>
+ */
+static l_int32
+pixacompExtendArray(PIXAC *pixac)
+{
+size_t oldsize, newsize;
+
+ PROCNAME("pixacompExtendArray");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ if (pixac->nalloc > MaxPtrArraySize) /* belt & suspenders */
+ return ERROR_INT("pixac has too many ptrs", procName, 1);
+ oldsize = pixac->nalloc * sizeof(PIXC *);
+ newsize = 2 * oldsize;
+ if (newsize > 8 * MaxPtrArraySize) /* ptrs for 1M pixcomp */
+ return ERROR_INT("newsize > 8 MB; too large", procName, 1);
+
+ if ((pixac->pixc = (PIXC **)reallocNew((void **)&pixac->pixc,
+ oldsize, newsize)) == NULL)
+ return ERROR_INT("new ptr array not returned", procName, 1);
+ pixac->nalloc *= 2;
+ boxaExtendArray(pixac->boxa);
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompReplacePix()
+ *
+ * \param[in] pixac
+ * \param[in] index caller's view of index within pixac; includes offset
+ * \param[in] pix owned by the caller
+ * \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
+ * \return 0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The %index includes the offset, which must be subtracted
+ * to get the actual index into the ptr array.
+ * (2) The input %pix is converted to a pixc, which is then inserted
+ * into the pixac.
+ * </pre>
+ */
+l_ok
+pixacompReplacePix(PIXAC *pixac,
+ l_int32 index,
+ PIX *pix,
+ l_int32 comptype)
+{
+l_int32 n, aindex;
+PIXC *pixc;
+
+ PROCNAME("pixacompReplacePix");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ n = pixacompGetCount(pixac);
+ aindex = index - pixac->offset;
+ if (aindex < 0 || aindex >= n)
+ return ERROR_INT("array index out of bounds", procName, 1);
+ if (!pix)
+ return ERROR_INT("pix not defined", procName, 1);
+ if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
+ comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
+ return ERROR_INT("invalid format", procName, 1);
+
+ pixc = pixcompCreateFromPix(pix, comptype);
+ pixacompReplacePixcomp(pixac, index, pixc);
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompReplacePixcomp()
+ *
+ * \param[in] pixac
+ * \param[in] index caller's view of index within pixac; includes offset
+ * \param[in] pixc to replace existing one, which is destroyed
+ * \return 0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The %index includes the offset, which must be subtracted
+ * to get the actual index into the ptr array.
+ * (2) The inserted %pixc is now owned by the pixac. The caller
+ * must not destroy it.
+ * </pre>
+ */
+l_ok
+pixacompReplacePixcomp(PIXAC *pixac,
+ l_int32 index,
+ PIXC *pixc)
+{
+l_int32 n, aindex;
+PIXC *pixct;
+
+ PROCNAME("pixacompReplacePixcomp");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ n = pixacompGetCount(pixac);
+ aindex = index - pixac->offset;
+ if (aindex < 0 || aindex >= n)
+ return ERROR_INT("array index out of bounds", procName, 1);
+ if (!pixc)
+ return ERROR_INT("pixc not defined", procName, 1);
+
+ pixct = pixacompGetPixcomp(pixac, index, L_NOCOPY); /* use %index */
+ pixcompDestroy(&pixct);
+ pixac->pixc[aindex] = pixc; /* replace; use array index */
+
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompAddBox()
+ *
+ * \param[in] pixac
+ * \param[in] box
+ * \param[in] copyflag L_INSERT, L_COPY
+ * \return 0 if OK, 1 on error
+ */
+l_ok
+pixacompAddBox(PIXAC *pixac,
+ BOX *box,
+ l_int32 copyflag)
+{
+ PROCNAME("pixacompAddBox");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ if (!box)
+ return ERROR_INT("box not defined", procName, 1);
+ if (copyflag != L_INSERT && copyflag != L_COPY)
+ return ERROR_INT("invalid copyflag", procName, 1);
+
+ boxaAddBox(pixac->boxa, box, copyflag);
+ return 0;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixacomp accessors *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixacompGetCount()
+ *
+ * \param[in] pixac
+ * \return count, or 0 if no pixa
+ */
+l_int32
+pixacompGetCount(PIXAC *pixac)
+{
+ PROCNAME("pixacompGetCount");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 0);
+
+ return pixac->n;
+}
+
+
+/*!
+ * \brief pixacompGetPixcomp()
+ *
+ * \param[in] pixac
+ * \param[in] index caller's view of index within pixac; includes offset
+ * \param[in] copyflag L_NOCOPY, L_COPY
+ * \return pixc, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The %index includes the offset, which must be subtracted
+ * to get the actual index into the ptr array.
+ * (2) If copyflag == L_NOCOPY, the pixc is owned by %pixac; do
+ * not destroy.
+ * </pre>
+ */
+PIXC *
+pixacompGetPixcomp(PIXAC *pixac,
+ l_int32 index,
+ l_int32 copyflag)
+{
+l_int32 aindex;
+
+ PROCNAME("pixacompGetPixcomp");
+
+ if (!pixac)
+ return (PIXC *)ERROR_PTR("pixac not defined", procName, NULL);
+ if (copyflag != L_NOCOPY && copyflag != L_COPY)
+ return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL);
+ aindex = index - pixac->offset;
+ if (aindex < 0 || aindex >= pixac->n)
+ return (PIXC *)ERROR_PTR("array index not valid", procName, NULL);
+
+ if (copyflag == L_NOCOPY)
+ return pixac->pixc[aindex];
+ else /* L_COPY */
+ return pixcompCopy(pixac->pixc[aindex]);
+}
+
+
+/*!
+ * \brief pixacompGetPix()
+ *
+ * \param[in] pixac
+ * \param[in] index caller's view of index within pixac; includes offset
+ * \return pix, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The %index includes the offset, which must be subtracted
+ * to get the actual index into the ptr array.
+ * </pre>
+ */
+PIX *
+pixacompGetPix(PIXAC *pixac,
+ l_int32 index)
+{
+l_int32 aindex;
+PIXC *pixc;
+
+ PROCNAME("pixacompGetPix");
+
+ if (!pixac)
+ return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
+ aindex = index - pixac->offset;
+ if (aindex < 0 || aindex >= pixac->n)
+ return (PIX *)ERROR_PTR("array index not valid", procName, NULL);
+
+ pixc = pixacompGetPixcomp(pixac, index, L_NOCOPY);
+ return pixCreateFromPixcomp(pixc);
+}
+
+
+/*!
+ * \brief pixacompGetPixDimensions()
+ *
+ * \param[in] pixac
+ * \param[in] index caller's view of index within pixac;
+ * includes offset
+ * \param[out] pw, ph, pd [optional] each can be null
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The %index includes the offset, which must be subtracted
+ * to get the actual index into the ptr array.
+ * </pre>
+ */
+l_ok
+pixacompGetPixDimensions(PIXAC *pixac,
+ l_int32 index,
+ l_int32 *pw,
+ l_int32 *ph,
+ l_int32 *pd)
+{
+l_int32 aindex;
+PIXC *pixc;
+
+ PROCNAME("pixacompGetPixDimensions");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ aindex = index - pixac->offset;
+ if (aindex < 0 || aindex >= pixac->n)
+ return ERROR_INT("array index not valid", procName, 1);
+
+ if ((pixc = pixac->pixc[aindex]) == NULL)
+ return ERROR_INT("pixc not found!", procName, 1);
+ pixcompGetDimensions(pixc, pw, ph, pd);
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompGetBoxa()
+ *
+ * \param[in] pixac
+ * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE
+ * \return boxa, or NULL on error
+ */
+BOXA *
+pixacompGetBoxa(PIXAC *pixac,
+ l_int32 accesstype)
+{
+ PROCNAME("pixacompGetBoxa");
+
+ if (!pixac)
+ return (BOXA *)ERROR_PTR("pixac not defined", procName, NULL);
+ if (!pixac->boxa)
+ return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL);
+ if (accesstype != L_COPY && accesstype != L_CLONE &&
+ accesstype != L_COPY_CLONE)
+ return (BOXA *)ERROR_PTR("invalid accesstype", procName, NULL);
+
+ return boxaCopy(pixac->boxa, accesstype);
+}
+
+
+/*!
+ * \brief pixacompGetBoxaCount()
+ *
+ * \param[in] pixac
+ * \return count, or 0 on error
+ */
+l_int32
+pixacompGetBoxaCount(PIXAC *pixac)
+{
+ PROCNAME("pixacompGetBoxaCount");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 0);
+
+ return boxaGetCount(pixac->boxa);
+}
+
+
+/*!
+ * \brief pixacompGetBox()
+ *
+ * \param[in] pixac
+ * \param[in] index caller's view of index within pixac;
+ * includes offset
+ * \param[in] accesstype L_COPY or L_CLONE
+ * \return box if null, not automatically an error, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The %index includes the offset, which must be subtracted
+ * to get the actual index into the ptr array.
+ * (2) There is always a boxa with a pixac, and it is initialized so
+ * that each box ptr is NULL.
+ * (3) In general, we expect that there is either a box associated
+ * with each pixc, or no boxes at all in the boxa.
+ * (4) Having no boxes is thus not an automatic error. Whether it
+ * is an actual error is determined by the calling program.
+ * If the caller expects to get a box, it is an error; see, e.g.,
+ * pixacGetBoxGeometry().
+ * </pre>
+ */
+BOX *
+pixacompGetBox(PIXAC *pixac,
+ l_int32 index,
+ l_int32 accesstype)
+{
+l_int32 aindex;
+BOX *box;
+
+ PROCNAME("pixacompGetBox");
+
+ if (!pixac)
+ return (BOX *)ERROR_PTR("pixac not defined", procName, NULL);
+ if (!pixac->boxa)
+ return (BOX *)ERROR_PTR("boxa not defined", procName, NULL);
+ aindex = index - pixac->offset;
+ if (aindex < 0 || aindex >= pixac->boxa->n)
+ return (BOX *)ERROR_PTR("array index not valid", procName, NULL);
+ if (accesstype != L_COPY && accesstype != L_CLONE)
+ return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL);
+
+ box = pixac->boxa->box[aindex];
+ if (box) {
+ if (accesstype == L_COPY)
+ return boxCopy(box);
+ else /* accesstype == L_CLONE */
+ return boxClone(box);
+ } else {
+ return NULL;
+ }
+}
+
+
+/*!
+ * \brief pixacompGetBoxGeometry()
+ *
+ * \param[in] pixac
+ * \param[in] index caller's view of index within pixac;
+ * includes offset
+ * \param[out] px, py, pw, ph [optional] each can be null
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The %index includes the offset, which must be subtracted
+ * to get the actual index into the ptr array.
+ * </pre>
+ */
+l_ok
+pixacompGetBoxGeometry(PIXAC *pixac,
+ l_int32 index,
+ l_int32 *px,
+ l_int32 *py,
+ l_int32 *pw,
+ l_int32 *ph)
+{
+l_int32 aindex;
+BOX *box;
+
+ PROCNAME("pixacompGetBoxGeometry");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ aindex = index - pixac->offset;
+ if (aindex < 0 || aindex >= pixac->n)
+ return ERROR_INT("array index not valid", procName, 1);
+
+ if ((box = pixacompGetBox(pixac, aindex, L_CLONE)) == NULL)
+ return ERROR_INT("box not found!", procName, 1);
+ boxGetGeometry(box, px, py, pw, ph);
+ boxDestroy(&box);
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompGetOffset()
+ *
+ * \param[in] pixac
+ * \return offset, or 0 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The offset is the difference between the caller's view of
+ * the index into the array and the actual array index.
+ * By default it is 0.
+ * </pre>
+ */
+l_int32
+pixacompGetOffset(PIXAC *pixac)
+{
+ PROCNAME("pixacompGetOffset");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 0);
+ return pixac->offset;
+}
+
+
+/*!
+ * \brief pixacompSetOffset()
+ *
+ * \param[in] pixac
+ * \param[in] offset non-negative
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The offset is the difference between the caller's view of
+ * the index into the array and the actual array index.
+ * By default it is 0.
+ * </pre>
+ */
+l_ok
+pixacompSetOffset(PIXAC *pixac,
+ l_int32 offset)
+{
+ PROCNAME("pixacompSetOffset");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ pixac->offset = L_MAX(0, offset);
+ return 0;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixacomp conversion to Pixa *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixaCreateFromPixacomp()
+ *
+ * \param[in] pixac
+ * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE; for boxa
+ * \return pixa if OK, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Because the pixa has no notion of offset, the offset must
+ * be set to 0 before the conversion, so that pixacompGetPix()
+ * fetches all the pixcomps. It is reset at the end.
+ * </pre>
+ */
+PIXA *
+pixaCreateFromPixacomp(PIXAC *pixac,
+ l_int32 accesstype)
+{
+l_int32 i, n, offset;
+PIX *pix;
+PIXA *pixa;
+
+ PROCNAME("pixaCreateFromPixacomp");
+
+ if (!pixac)
+ return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL);
+ if (accesstype != L_COPY && accesstype != L_CLONE &&
+ accesstype != L_COPY_CLONE)
+ return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL);
+
+ n = pixacompGetCount(pixac);
+ offset = pixacompGetOffset(pixac);
+ pixacompSetOffset(pixac, 0);
+ if ((pixa = pixaCreate(n)) == NULL)
+ return (PIXA *)ERROR_PTR("pixa not made", procName, NULL);
+ for (i = 0; i < n; i++) {
+ if ((pix = pixacompGetPix(pixac, i)) == NULL) {
+ L_WARNING("pix %d not made\n", procName, i);
+ continue;
+ }
+ pixaAddPix(pixa, pix, L_INSERT);
+ }
+ if (pixa->boxa) {
+ boxaDestroy(&pixa->boxa);
+ pixa->boxa = pixacompGetBoxa(pixac, accesstype);
+ }
+ pixacompSetOffset(pixac, offset);
+
+ return pixa;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Combining pixacomp
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixacompJoin()
+ *
+ * \param[in] pixacd dest pixac; add to this one
+ * \param[in] pixacs [optional] source pixac; add from this one
+ * \param[in] istart starting index in pixacs
+ * \param[in] iend ending index in pixacs; use -1 to cat all
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) This appends a clone of each indicated pixc in pixcas to pixcad
+ * (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
+ * (3) iend < 0 means 'read to the end'
+ * (4) If pixacs is NULL or contains no pixc, this is a no-op.
+ * </pre>
+ */
+l_ok
+pixacompJoin(PIXAC *pixacd,
+ PIXAC *pixacs,
+ l_int32 istart,
+ l_int32 iend)
+{
+l_int32 i, n, nb;
+BOXA *boxas, *boxad;
+PIXC *pixc;
+
+ PROCNAME("pixacompJoin");
+
+ if (!pixacd)
+ return ERROR_INT("pixacd not defined", procName, 1);
+ if (!pixacs || ((n = pixacompGetCount(pixacs)) == 0))
+ return 0;
+
+ if (istart < 0)
+ istart = 0;
+ if (iend < 0 || iend >= n)
+ iend = n - 1;
+ if (istart > iend)
+ return ERROR_INT("istart > iend; nothing to add", procName, 1);
+
+ for (i = istart; i <= iend; i++) {
+ pixc = pixacompGetPixcomp(pixacs, i, L_NOCOPY);
+ pixacompAddPixcomp(pixacd, pixc, L_COPY);
+ }
+
+ boxas = pixacompGetBoxa(pixacs, L_CLONE);
+ boxad = pixacompGetBoxa(pixacd, L_CLONE);
+ nb = pixacompGetBoxaCount(pixacs);
+ iend = L_MIN(iend, nb - 1);
+ boxaJoin(boxad, boxas, istart, iend);
+ boxaDestroy(&boxas); /* just the clones */
+ boxaDestroy(&boxad); /* ditto */
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompInterleave()
+ *
+ * \param[in] pixac1 first src pixac
+ * \param[in] pixac2 second src pixac
+ * \return pixacd interleaved from sources, or NULL on error.
+ *
+ * <pre>
+ * Notes:
+ * (1) If the two pixac have different sizes, a warning is issued,
+ * and the number of pairs returned is the minimum size.
+ * </pre>
+ */
+PIXAC *
+pixacompInterleave(PIXAC *pixac1,
+ PIXAC *pixac2)
+{
+l_int32 i, n1, n2, n, nb1, nb2;
+BOX *box;
+PIXC *pixc1, *pixc2;
+PIXAC *pixacd;
+
+ PROCNAME("pixacompInterleave");
+
+ if (!pixac1)
+ return (PIXAC *)ERROR_PTR("pixac1 not defined", procName, NULL);
+ if (!pixac2)
+ return (PIXAC *)ERROR_PTR("pixac2 not defined", procName, NULL);
+ n1 = pixacompGetCount(pixac1);
+ n2 = pixacompGetCount(pixac2);
+ n = L_MIN(n1, n2);
+ if (n == 0)
+ return (PIXAC *)ERROR_PTR("at least one input pixac is empty",
+ procName, NULL);
+ if (n1 != n2)
+ L_WARNING("counts differ: %d != %d\n", procName, n1, n2);
+
+ pixacd = pixacompCreate(2 * n);
+ nb1 = pixacompGetBoxaCount(pixac1);
+ nb2 = pixacompGetBoxaCount(pixac2);
+ for (i = 0; i < n; i++) {
+ pixc1 = pixacompGetPixcomp(pixac1, i, L_COPY);
+ pixacompAddPixcomp(pixacd, pixc1, L_INSERT);
+ if (i < nb1) {
+ box = pixacompGetBox(pixac1, i, L_COPY);
+ pixacompAddBox(pixacd, box, L_INSERT);
+ }
+ pixc2 = pixacompGetPixcomp(pixac2, i, L_COPY);
+ pixacompAddPixcomp(pixacd, pixc2, L_INSERT);
+ if (i < nb2) {
+ box = pixacompGetBox(pixac2, i, L_COPY);
+ pixacompAddBox(pixacd, box, L_INSERT);
+ }
+ }
+
+ return pixacd;
+}
+
+
+/*---------------------------------------------------------------------*
+ * Pixacomp serialized I/O *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief pixacompRead()
+ *
+ * \param[in] filename
+ * \return pixac, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Unlike the situation with serialized Pixa, where the image
+ * data is stored in png format, the Pixacomp image data
+ * can be stored in tiffg4, png and jpg formats.
+ * </pre>
+ */
+PIXAC *
+pixacompRead(const char *filename)
+{
+FILE *fp;
+PIXAC *pixac;
+
+ PROCNAME("pixacompRead");
+
+ if (!filename)
+ return (PIXAC *)ERROR_PTR("filename not defined", procName, NULL);
+
+ if ((fp = fopenReadStream(filename)) == NULL)
+ return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL);
+ pixac = pixacompReadStream(fp);
+ fclose(fp);
+ if (!pixac)
+ return (PIXAC *)ERROR_PTR("pixac not read", procName, NULL);
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompReadStream()
+ *
+ * \param[in] fp file stream
+ * \return pixac, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) It is OK for the pixacomp to be empty.
+ * </pre>
+ */
+PIXAC *
+pixacompReadStream(FILE *fp)
+{
+char buf[256];
+l_uint8 *data;
+l_int32 n, offset, i, w, h, d, ignore;
+l_int32 comptype, cmapflag, version, xres, yres;
+size_t size;
+BOXA *boxa;
+PIXC *pixc;
+PIXAC *pixac;
+
+ PROCNAME("pixacompReadStream");
+
+ if (!fp)
+ return (PIXAC *)ERROR_PTR("stream not defined", procName, NULL);
+
+ if (fscanf(fp, "\nPixacomp Version %d\n", &version) != 1)
+ return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL);
+ if (version != PIXACOMP_VERSION_NUMBER)
+ return (PIXAC *)ERROR_PTR("invalid pixacomp version", procName, NULL);
+ if (fscanf(fp, "Number of pixcomp = %d\n", &n) != 1)
+ return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL);
+ if (fscanf(fp, "Offset of index into array = %d", &offset) != 1)
+ return (PIXAC *)ERROR_PTR("offset not read", procName, NULL);
+ if (n < 0)
+ return (PIXAC *)ERROR_PTR("num pixcomp ptrs < 0", procName, NULL);
+ if (n > MaxPtrArraySize)
+ return (PIXAC *)ERROR_PTR("too many pixcomp ptrs", procName, NULL);
+ if (n == 0) L_INFO("the pixacomp is empty\n", procName);
+
+ if ((pixac = pixacompCreate(n)) == NULL)
+ return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
+ if ((boxa = boxaReadStream(fp)) == NULL) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
+ }
+ boxaDestroy(&pixac->boxa); /* empty */
+ pixac->boxa = boxa;
+ pixacompSetOffset(pixac, offset);
+
+ for (i = 0; i < n; i++) {
+ if (fscanf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n",
+ &ignore, &w, &h, &d) != 4) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("dimension reading", procName, NULL);
+ }
+ if (fscanf(fp, " comptype = %d, size = %zu, cmapflag = %d\n",
+ &comptype, &size, &cmapflag) != 3) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("comptype/size reading", procName, NULL);
+ }
+ if (size > MaxDataSize) {
+ pixacompDestroy(&pixac);
+ L_ERROR("data size = %zu is too big", procName, size);
+ return NULL;
+ }
+
+ /* Use fgets() and sscanf(); not fscanf(), for the last
+ * bit of header data before the binary data. The reason is
+ * that fscanf throws away white space, and if the binary data
+ * happens to begin with ascii character(s) that are white
+ * space, it will swallow them and all will be lost! */
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("fgets read fail", procName, NULL);
+ }
+ if (sscanf(buf, " xres = %d, yres = %d\n", &xres, &yres) != 2) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("read fail for res", procName, NULL);
+ }
+ if ((data = (l_uint8 *)LEPT_CALLOC(1, size)) == NULL) {
+ pixacompDestroy(&pixac);
+ return (PIXAC *)ERROR_PTR("calloc fail for data", procName, NULL);
+ }
+ if (fread(data, 1, size, fp) != size) {
+ pixacompDestroy(&pixac);
+ LEPT_FREE(data);
+ return (PIXAC *)ERROR_PTR("error reading data", procName, NULL);
+ }
+ fgetc(fp); /* swallow the ending nl */
+ pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
+ pixc->w = w;
+ pixc->h = h;
+ pixc->d = d;
+ pixc->xres = xres;
+ pixc->yres = yres;
+ pixc->comptype = comptype;
+ pixc->cmapflag = cmapflag;
+ pixc->data = data;
+ pixc->size = size;
+ pixacompAddPixcomp(pixac, pixc, L_INSERT);
+ }
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompReadMem()
+ *
+ * \param[in] data in pixacomp format
+ * \param[in] size of data
+ * \return pixac, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Deseralizes a buffer of pixacomp data into a pixac in memory.
+ * </pre>
+ */
+PIXAC *
+pixacompReadMem(const l_uint8 *data,
+ size_t size)
+{
+FILE *fp;
+PIXAC *pixac;
+
+ PROCNAME("pixacompReadMem");
+
+ if (!data)
+ return (PIXAC *)ERROR_PTR("data not defined", procName, NULL);
+ if ((fp = fopenReadFromMemory(data, size)) == NULL)
+ return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL);
+
+ pixac = pixacompReadStream(fp);
+ fclose(fp);
+ if (!pixac) L_ERROR("pixac not read\n", procName);
+ return pixac;
+}
+
+
+/*!
+ * \brief pixacompWrite()
+ *
+ * \param[in] filename
+ * \param[in] pixac
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Unlike the situation with serialized Pixa, where the image
+ * data is stored in png format, the Pixacomp image data
+ * can be stored in tiffg4, png and jpg formats.
+ * </pre>
+ */
+l_ok
+pixacompWrite(const char *filename,
+ PIXAC *pixac)
+{
+l_int32 ret;
+FILE *fp;
+
+ PROCNAME("pixacompWrite");
+
+ if (!filename)
+ return ERROR_INT("filename not defined", procName, 1);
+ if (!pixac)
+ return ERROR_INT("pixacomp not defined", procName, 1);
+
+ if ((fp = fopenWriteStream(filename, "wb")) == NULL)
+ return ERROR_INT("stream not opened", procName, 1);
+ ret = pixacompWriteStream(fp, pixac);
+ fclose(fp);
+ if (ret)
+ return ERROR_INT("pixacomp not written to stream", procName, 1);
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompWriteStream()
+ *
+ * \param[in] fp file stream
+ * \param[in] pixac
+ * \return 0 if OK, 1 on error
+ */
+l_ok
+pixacompWriteStream(FILE *fp,
+ PIXAC *pixac)
+{
+l_int32 n, i;
+PIXC *pixc;
+
+ PROCNAME("pixacompWriteStream");
+
+ if (!fp)
+ return ERROR_INT("stream not defined", procName, 1);
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+
+ n = pixacompGetCount(pixac);
+ fprintf(fp, "\nPixacomp Version %d\n", PIXACOMP_VERSION_NUMBER);
+ fprintf(fp, "Number of pixcomp = %d\n", n);
+ fprintf(fp, "Offset of index into array = %d", pixac->offset);
+ boxaWriteStream(fp, pixac->boxa);
+ for (i = 0; i < n; i++) {
+ if ((pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY))
+ == NULL)
+ return ERROR_INT("pixc not found", procName, 1);
+ fprintf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n",
+ i, pixc->w, pixc->h, pixc->d);
+ fprintf(fp, " comptype = %d, size = %zu, cmapflag = %d\n",
+ pixc->comptype, pixc->size, pixc->cmapflag);
+ fprintf(fp, " xres = %d, yres = %d\n", pixc->xres, pixc->yres);
+ fwrite(pixc->data, 1, pixc->size, fp);
+ fprintf(fp, "\n");
+ }
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompWriteMem()
+ *
+ * \param[out] pdata serialized data of pixac
+ * \param[out] psize size of serialized data
+ * \param[in] pixac
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) Serializes a pixac in memory and puts the result in a buffer.
+ * </pre>
+ */
+l_ok
+pixacompWriteMem(l_uint8 **pdata,
+ size_t *psize,
+ PIXAC *pixac)
+{
+l_int32 ret;
+FILE *fp;
+
+ PROCNAME("pixacompWriteMem");
+
+ if (pdata) *pdata = NULL;
+ if (psize) *psize = 0;
+ if (!pdata)
+ return ERROR_INT("&data not defined", procName, 1);
+ if (!psize)
+ return ERROR_INT("&size not defined", procName, 1);
+ if (!pixac)
+ return ERROR_INT("&pixac not defined", procName, 1);
+
+#if HAVE_FMEMOPEN
+ if ((fp = open_memstream((char **)pdata, psize)) == NULL)
+ return ERROR_INT("stream not opened", procName, 1);
+ ret = pixacompWriteStream(fp, pixac);
+#else
+ L_INFO("work-around: writing to a temp file\n", procName);
+ #ifdef _WIN32
+ if ((fp = fopenWriteWinTempfile()) == NULL)
+ return ERROR_INT("tmpfile stream not opened", procName, 1);
+ #else
+ if ((fp = tmpfile()) == NULL)
+ return ERROR_INT("tmpfile stream not opened", procName, 1);
+ #endif /* _WIN32 */
+ ret = pixacompWriteStream(fp, pixac);
+ rewind(fp);
+ *pdata = l_binaryReadStream(fp, psize);
+#endif /* HAVE_FMEMOPEN */
+ fclose(fp);
+ return ret;
+}
+
+
+/*--------------------------------------------------------------------*
+ * Conversion to pdf *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief pixacompConvertToPdf()
+ *
+ * \param[in] pixac containing images all at the same resolution
+ * \param[in] res override the resolution of each input image,
+ * in ppi; 0 to respect the resolution embedded
+ * in the input
+ * \param[in] scalefactor scaling factor applied to each image; > 0.0
+ * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE,
+ * L_FLATE_ENCODE, L_JP2K_ENCODE, or
+ * L_DEFAULT_ENCODE for default)
+ * \param[in] quality used for JPEG only; 0 for default (75)
+ * \param[in] title [optional] pdf title
+ * \param[in] fileout pdf file of all images
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) This follows closely the function pixaConvertToPdf() in pdfio.c.
+ * (2) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without
+ * colormap and many colors, or 32 bpp; FLATE for anything else.
+ * (3) The scalefactor must be > 0.0; otherwise it is set to 1.0.
+ * (4) Specifying one of the three encoding types for %type forces
+ * all images to be compressed with that type. Use 0 to have
+ * the type determined for each image based on depth and whether
+ * or not it has a colormap.
+ * (5) If all images are jpeg compressed, don't require scaling
+ * and have the same resolution, it is much faster to skip
+ * transcoding with pixacompFastConvertToPdfData(), and then
+ * write the data out to file.
+ * </pre>
+ */
+l_ok
+pixacompConvertToPdf(PIXAC *pixac,
+ l_int32 res,
+ l_float32 scalefactor,
+ l_int32 type,
+ l_int32 quality,
+ const char *title,
+ const char *fileout)
+{
+l_uint8 *data;
+l_int32 ret;
+size_t nbytes;
+
+ PROCNAME("pixacompConvertToPdf");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+
+ ret = pixacompConvertToPdfData(pixac, res, scalefactor, type, quality,
+ title, &data, &nbytes);
+ if (ret) {
+ LEPT_FREE(data);
+ return ERROR_INT("conversion to pdf failed", procName, 1);
+ }
+
+ ret = l_binaryWrite(fileout, "w", data, nbytes);
+ LEPT_FREE(data);
+ if (ret)
+ L_ERROR("pdf data not written to file\n", procName);
+ return ret;
+}
+
+
+/*!
+ * \brief pixacompConvertToPdfData()
+ *
+ * \param[in] pixac containing images all at the same resolution
+ * \param[in] res input resolution of all images
+ * \param[in] scalefactor scaling factor applied to each image; > 0.0
+ * \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE,
+ * L_FLATE_ENCODE, L_JP2K_ENCODE, or
+ * L_DEFAULT_ENCODE for default)
+ * \param[in] quality used for JPEG only; 0 for default (75)
+ * \param[in] title [optional] pdf title
+ * \param[out] pdata output pdf data (of all images
+ * \param[out] pnbytes size of output pdf data
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) See pixacompConvertToPdf().
+ * </pre>
+ */
+l_ok
+pixacompConvertToPdfData(PIXAC *pixac,
+ l_int32 res,
+ l_float32 scalefactor,
+ l_int32 type,
+ l_int32 quality,
+ const char *title,
+ l_uint8 **pdata,
+ size_t *pnbytes)
+{
+l_uint8 *imdata;
+l_int32 i, n, ret, scaledres, pagetype;
+size_t imbytes;
+L_BYTEA *ba;
+PIX *pixs, *pix;
+L_PTRA *pa_data;
+
+ PROCNAME("pixacompConvertToPdfData");
+
+ if (!pdata)
+ return ERROR_INT("&data not defined", procName, 1);
+ *pdata = NULL;
+ if (!pnbytes)
+ return ERROR_INT("&nbytes not defined", procName, 1);
+ *pnbytes = 0;
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+ if (scalefactor <= 0.0) scalefactor = 1.0;
+ if (type != L_DEFAULT_ENCODE && type != L_JPEG_ENCODE &&
+ type != L_G4_ENCODE && type != L_FLATE_ENCODE &&
+ type != L_JP2K_ENCODE) {
+ L_WARNING("invalid compression type; using per-page default\n",
+ procName);
+ type = L_DEFAULT_ENCODE;
+ }
+
+ /* Generate all the encoded pdf strings */
+ n = pixacompGetCount(pixac);
+ pa_data = ptraCreate(n);
+ for (i = 0; i < n; i++) {
+ if ((pixs =
+ pixacompGetPix(pixac, pixacompGetOffset(pixac) + i)) == NULL) {
+ L_ERROR("pix[%d] not retrieved\n", procName, i);
+ continue;
+ }
+ if (pixGetWidth(pixs) == 1) { /* used sometimes as placeholders */
+ L_INFO("placeholder image[%d] has w = 1\n", procName, i);
+ pixDestroy(&pixs);
+ continue;
+ }
+ if (scalefactor != 1.0)
+ pix = pixScale(pixs, scalefactor, scalefactor);
+ else
+ pix = pixClone(pixs);
+ pixDestroy(&pixs);
+ scaledres = (l_int32)(res * scalefactor);
+
+ /* Select the encoding type */
+ if (type != L_DEFAULT_ENCODE) {
+ pagetype = type;
+ } else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) {
+ L_ERROR("encoding type selection failed for pix[%d]\n",
+ procName, i);
+ pixDestroy(&pix);
+ continue;
+ }
+
+ ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes,
+ 0, 0, scaledres, title, NULL, 0);
+ pixDestroy(&pix);
+ if (ret) {
+ L_ERROR("pdf encoding failed for pix[%d]\n", procName, i);
+ continue;
+ }
+ ba = l_byteaInitFromMem(imdata, imbytes);
+ LEPT_FREE(imdata);
+ ptraAdd(pa_data, ba);
+ }
+ ptraGetActualCount(pa_data, &n);
+ if (n == 0) {
+ L_ERROR("no pdf files made\n", procName);
+ ptraDestroy(&pa_data, FALSE, FALSE);
+ return 1;
+ }
+
+ /* Concatenate them */
+ ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes);
+
+ ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */
+ for (i = 0; i < n; i++) {
+ ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION);
+ l_byteaDestroy(&ba);
+ }
+ ptraDestroy(&pa_data, FALSE, FALSE);
+ return ret;
+}
+
+
+/*!
+ * \brief pixacompFastConvertToPdfData()
+ *
+ * \param[in] pixac containing images all at the same resolution
+ * \param[in] title [optional] pdf title
+ * \param[out] pdata output pdf data (of all images
+ * \param[out] pnbytes size of output pdf data
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) This generates the pdf without transcoding if all the
+ * images in %pixac are compressed with jpeg.
+ * Images not jpeg compressed are skipped.
+ * (2) It assumes all images have the same resolution, and that
+ * the resolution embedded in each jpeg file is correct.
+ * </pre>
+ */
+l_ok
+pixacompFastConvertToPdfData(PIXAC *pixac,
+ const char *title,
+ l_uint8 **pdata,
+ size_t *pnbytes)
+{
+l_uint8 *imdata;
+l_int32 i, n, ret, comptype;
+size_t imbytes;
+L_BYTEA *ba;
+PIXC *pixc;
+L_PTRA *pa_data;
+
+ PROCNAME("pixacompFastConvertToPdfData");
+
+ if (!pdata)
+ return ERROR_INT("&data not defined", procName, 1);
+ *pdata = NULL;
+ if (!pnbytes)
+ return ERROR_INT("&nbytes not defined", procName, 1);
+ *pnbytes = 0;
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+
+ /* Generate all the encoded pdf strings */
+ n = pixacompGetCount(pixac);
+ pa_data = ptraCreate(n);
+ for (i = 0; i < n; i++) {
+ if ((pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY)) == NULL) {
+ L_ERROR("pixc[%d] not retrieved\n", procName, i);
+ continue;
+ }
+ pixcompGetParameters(pixc, NULL, NULL, &comptype, NULL);
+ if (comptype != IFF_JFIF_JPEG) {
+ L_ERROR("pixc[%d] not jpeg compressed\n", procName, i);
+ continue;
+ }
+ ret = pixcompFastConvertToPdfData(pixc, title, &imdata, &imbytes);
+ if (ret) {
+ L_ERROR("pdf encoding failed for pixc[%d]\n", procName, i);
+ continue;
+ }
+ ba = l_byteaInitFromMem(imdata, imbytes);
+ LEPT_FREE(imdata);
+ ptraAdd(pa_data, ba);
+ }
+ ptraGetActualCount(pa_data, &n);
+ if (n == 0) {
+ L_ERROR("no pdf files made\n", procName);
+ ptraDestroy(&pa_data, FALSE, FALSE);
+ return 1;
+ }
+
+ /* Concatenate them */
+ ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes);
+
+ /* Clean up */
+ ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */
+ for (i = 0; i < n; i++) {
+ ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION);
+ l_byteaDestroy(&ba);
+ }
+ ptraDestroy(&pa_data, FALSE, FALSE);
+ return ret;
+}
+
+
+/*!
+ * \brief pixcompFastConvertToPdfData()
+ *
+ * \param[in] pixc containing images all at the same resolution
+ * \param[in] title [optional] pdf title
+ * \param[out] pdata output pdf data (of all images
+ * \param[out] pnbytes size of output pdf data
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) This generates the pdf without transcoding.
+ * (2) It assumes all images are jpeg encoded, have the same
+ * resolution, and that the resolution embedded in each
+ * jpeg file is correct. (It is transferred to the pdf
+ * via the cid.)
+ * </pre>
+ */
+static l_int32
+pixcompFastConvertToPdfData(PIXC *pixc,
+ const char *title,
+ l_uint8 **pdata,
+ size_t *pnbytes)
+{
+l_uint8 *data;
+L_COMP_DATA *cid;
+
+ PROCNAME("pixacompFastConvertToPdfData");
+
+ if (!pdata)
+ return ERROR_INT("&data not defined", procName, 1);
+ *pdata = NULL;
+ if (!pnbytes)
+ return ERROR_INT("&nbytes not defined", procName, 1);
+ *pnbytes = 0;
+ if (!pixc)
+ return ERROR_INT("pixc not defined", procName, 1);
+
+ /* Make a copy of the data */
+ data = l_binaryCopy(pixc->data, pixc->size);
+ cid = l_generateJpegDataMem(data, pixc->size, 0);
+
+ /* Note: cid is destroyed, along with data, by this function */
+ return cidConvertToPdfData(cid, title, pdata, pnbytes);
+}
+
+
+/*--------------------------------------------------------------------*
+ * Output for debugging *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief pixacompWriteStreamInfo()
+ *
+ * \param[in] fp file stream
+ * \param[in] pixac
+ * \param[in] text [optional] identifying string; can be null
+ * \return 0 if OK, 1 on error
+ */
+l_ok
+pixacompWriteStreamInfo(FILE *fp,
+ PIXAC *pixac,
+ const char *text)
+{
+l_int32 i, n, nboxes;
+PIXC *pixc;
+
+ PROCNAME("pixacompWriteStreamInfo");
+
+ if (!fp)
+ return ERROR_INT("fp not defined", procName, 1);
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+
+ if (text)
+ fprintf(fp, "Pixacomp Info for %s:\n", text);
+ else
+ fprintf(fp, "Pixacomp Info:\n");
+ n = pixacompGetCount(pixac);
+ nboxes = pixacompGetBoxaCount(pixac);
+ fprintf(fp, "Number of pixcomp: %d\n", n);
+ fprintf(fp, "Size of pixcomp array alloc: %d\n", pixac->nalloc);
+ fprintf(fp, "Offset of index into array: %d\n", pixac->offset);
+ if (nboxes > 0)
+ fprintf(fp, "Boxa has %d boxes\n", nboxes);
+ else
+ fprintf(fp, "Boxa is empty\n");
+ for (i = 0; i < n; i++) {
+ pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY);
+ pixcompWriteStreamInfo(fp, pixc, NULL);
+ }
+ return 0;
+}
+
+
+/*!
+ * \brief pixcompWriteStreamInfo()
+ *
+ * \param[in] fp file stream
+ * \param[in] pixc
+ * \param[in] text [optional] identifying string; can be null
+ * \return 0 if OK, 1 on error
+ */
+l_ok
+pixcompWriteStreamInfo(FILE *fp,
+ PIXC *pixc,
+ const char *text)
+{
+ PROCNAME("pixcompWriteStreamInfo");
+
+ if (!fp)
+ return ERROR_INT("fp not defined", procName, 1);
+ if (!pixc)
+ return ERROR_INT("pixc not defined", procName, 1);
+
+ if (text)
+ fprintf(fp, " Pixcomp Info for %s:", text);
+ else
+ fprintf(fp, " Pixcomp Info:");
+ fprintf(fp, " width = %d, height = %d, depth = %d\n",
+ pixc->w, pixc->h, pixc->d);
+ fprintf(fp, " xres = %d, yres = %d, size in bytes = %zu\n",
+ pixc->xres, pixc->yres, pixc->size);
+ if (pixc->cmapflag)
+ fprintf(fp, " has colormap\n");
+ else
+ fprintf(fp, " no colormap\n");
+ if (pixc->comptype < NumImageFileFormatExtensions) {
+ fprintf(fp, " comptype = %s (%d)\n",
+ ImageFileFormatExtensions[pixc->comptype], pixc->comptype);
+ } else {
+ fprintf(fp, " Error!! Invalid comptype index: %d\n", pixc->comptype);
+ }
+ return 0;
+}
+
+
+/*!
+ * \brief pixacompDisplayTiledAndScaled()
+ *
+ * \param[in] pixac
+ * \param[in] outdepth output depth: 1, 8 or 32 bpp
+ * \param[in] tilewidth each pix is scaled to this width
+ * \param[in] ncols number of tiles in each row
+ * \param[in] background 0 for white, 1 for black; this is the color
+ * of the spacing between the images
+ * \param[in] spacing between images, and on outside
+ * \param[in] border width of additional black border on each image;
+ * use 0 for no border
+ * \return pix of tiled images, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ * (1) This is the same function as pixaDisplayTiledAndScaled(),
+ * except it works on a Pixacomp instead of a Pix. It is particularly
+ * useful for showing the images in a Pixacomp at reduced resolution.
+ * (2) See pixaDisplayTiledAndScaled() for details.
+ * </pre>
+ */
+PIX *
+pixacompDisplayTiledAndScaled(PIXAC *pixac,
+ l_int32 outdepth,
+ l_int32 tilewidth,
+ l_int32 ncols,
+ l_int32 background,
+ l_int32 spacing,
+ l_int32 border)
+{
+PIX *pixd;
+PIXA *pixa;
+
+ PROCNAME("pixacompDisplayTiledAndScaled");
+
+ if (!pixac)
+ return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
+
+ if ((pixa = pixaCreateFromPixacomp(pixac, L_COPY)) == NULL)
+ return (PIX *)ERROR_PTR("pixa not made", procName, NULL);
+
+ pixd = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols,
+ background, spacing, border);
+ pixaDestroy(&pixa);
+ return pixd;
+}
+
+
+/*!
+ * \brief pixacompWriteFiles()
+ *
+ * \param[in] pixac
+ * \param[in] subdir subdirectory of /tmp
+ * \return 0 if OK, 1 on error
+ */
+l_ok
+pixacompWriteFiles(PIXAC *pixac,
+ const char *subdir)
+{
+char buf[128];
+l_int32 i, n;
+PIXC *pixc;
+
+ PROCNAME("pixacompWriteFiles");
+
+ if (!pixac)
+ return ERROR_INT("pixac not defined", procName, 1);
+
+ if (lept_mkdir(subdir) > 0)
+ return ERROR_INT("invalid subdir", procName, 1);
+
+ n = pixacompGetCount(pixac);
+ for (i = 0; i < n; i++) {
+ pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY);
+ snprintf(buf, sizeof(buf), "/tmp/%s/%03d", subdir, i);
+ pixcompWriteFile(buf, pixc);
+ }
+ return 0;
+}
+
+extern const char *ImageFileFormatExtensions[];
+
+/*!
+ * \brief pixcompWriteFile()
+ *
+ * \param[in] rootname
+ * \param[in] pixc
+ * \return 0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ * (1) The compressed data is written to file, and the filename is
+ * generated by appending the format extension to %rootname.
+ * </pre>
+ */
+l_ok
+pixcompWriteFile(const char *rootname,
+ PIXC *pixc)
+{
+char buf[128];
+
+ PROCNAME("pixcompWriteFile");
+
+ if (!pixc)
+ return ERROR_INT("pixc not defined", procName, 1);
+
+ snprintf(buf, sizeof(buf), "%s.%s", rootname,
+ ImageFileFormatExtensions[pixc->comptype]);
+ l_binaryWrite(buf, "w", pixc->data, pixc->size);
+ return 0;
+}