diff mupdf-source/thirdparty/leptonica/src/writefile.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/leptonica/src/writefile.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,1233 @@
+/*====================================================================*
+ -  Copyright (C) 2001-2016 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.
+ *====================================================================*/
+
+/*
+ * writefile.c
+ *
+ *     Set jpeg quality for pixWrite() and pixWriteMem()
+ *        l_int32     l_jpegSetQuality()
+ *
+ *     Set global variable LeptDebugOK for writing to named temp files
+ *        l_int32     setLeptDebugOK()
+ *
+ *     High-level procedures for writing images to file:
+ *        l_int32     pixaWriteFiles()
+ *        l_int32     pixWriteDebug()
+ *        l_int32     pixWrite()
+ *        l_int32     pixWriteAutoFormat()
+ *        l_int32     pixWriteStream()
+ *        l_int32     pixWriteImpliedFormat()
+ *
+ *     Selection of output format if default is requested
+ *        l_int32     pixChooseOutputFormat()
+ *        l_int32     getImpliedFileFormat()
+ *        l_int32     getFormatFromExtension()
+ *        l_int32     pixGetAutoFormat()
+ *        const char *getFormatExtension()
+ *
+ *     Write to memory
+ *        l_int32     pixWriteMem()
+ *
+ *     Image display for debugging
+ *        l_int32     l_fileDisplay()
+ *        l_int32     pixDisplay()
+ *        l_int32     pixDisplayWithTitle()
+ *        PIX        *pixMakeColorSquare()
+ *        void        l_chooseDisplayProg()
+ *
+ *     Change format for missing library
+ *        void        changeFormatForMissingLib()
+ *
+ *     Nonfunctional stub of pix output for debugging
+ *        l_int32     pixDisplayWrite()
+ *
+ *  Supported file formats:
+ *  (1) Writing is supported without any external libraries:
+ *          bmp
+ *          pnm   (including pbm, pgm, etc)
+ *          spix  (raw serialized)
+ *  (2) Writing is supported with installation of external libraries:
+ *          png
+ *          jpg   (standard jfif version)
+ *          tiff  (including most varieties of compression)
+ *          gif
+ *          webp
+ *          jp2 (jpeg2000)
+ *  (3) Writing is supported through special interfaces:
+ *          ps (PostScript, in psio1.c, psio2.c):
+ *              level 1 (uncompressed)
+ *              level 2 (g4 and dct encoding: requires tiff, jpg)
+ *              level 3 (g4, dct and flate encoding: requires tiff, jpg, zlib)
+ *          pdf (PDF, in pdfio.c):
+ *              level 1 (g4 and dct encoding: requires tiff, jpg)
+ *              level 2 (g4, dct and flate encoding: requires tiff, jpg, zlib)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config_auto.h>
+#endif  /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include "allheaders.h"
+
+    /* Set defaults for the display program (xv, xli, xzgv, open, irfanview)
+     * that is invoked by pixDisplay()  */
+#ifdef _WIN32
+static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_IV;  /* default */
+#elif  defined(__APPLE__)
+static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_OPEN;  /* default */
+#else
+static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_XZGV;  /* default */
+#endif  /* _WIN32 */
+
+#define Bufsize 512
+static const l_int32  MaxDisplayWidth = 1000;
+static const l_int32  MaxDisplayHeight = 800;
+static const l_int32  MaxSizeForPng = 200;
+
+    /* PostScript output for printing */
+static const l_float32  DefaultScaling = 1.0;
+
+    /* Global array of image file format extension names.                */
+    /* This is in 1-1 correspondence with format enum in imageio.h.      */
+    /* The empty string at the end represents the serialized format,     */
+    /* which has no recognizable extension name, but the array must      */
+    /* be padded to agree with the format enum.                          */
+    /* (Note on 'const': The size of the array can't be defined 'const'  */
+    /* because that makes it static.  The 'const' in the definition of   */
+    /* the array refers to the strings in the array; the ptr to the      */
+    /* array is not const and can be used 'extern' in other files.)      */
+LEPT_DLL l_int32  NumImageFileFormatExtensions = 20;  /* array size */
+LEPT_DLL const char *ImageFileFormatExtensions[] =
+         {"unknown",
+          "bmp",
+          "jpg",
+          "png",
+          "tif",
+          "tif",
+          "tif",
+          "tif",
+          "tif",
+          "tif",
+          "tif",
+          "pnm",
+          "ps",
+          "gif",
+          "jp2",
+          "webp",
+          "pdf",
+          "tif",
+          "default",
+          ""};
+
+    /* Local map of image file name extension to output format.
+     * Note that the extension string always includes a '.'  */
+struct ExtensionMap
+{
+    char     extension[16];
+    l_int32  format;
+};
+static const struct ExtensionMap extension_map[] =
+                            { { ".bmp",      IFF_BMP       },
+                              { ".jpg",      IFF_JFIF_JPEG },
+                              { ".jpeg",     IFF_JFIF_JPEG },
+                              { ".JPG",      IFF_JFIF_JPEG },
+                              { ".png",      IFF_PNG       },
+                              { ".tif",      IFF_TIFF      },
+                              { ".tiff",     IFF_TIFF      },
+                              { ".tiffg4",   IFF_TIFF_G4   },
+                              { ".pbm",      IFF_PNM       },
+                              { ".pgm",      IFF_PNM       },
+                              { ".pnm",      IFF_PNM       },
+                              { ".gif",      IFF_GIF       },
+                              { ".jp2",      IFF_JP2       },
+                              { ".j2k",      IFF_JP2       },
+                              { ".ps",       IFF_PS        },
+                              { ".pdf",      IFF_LPDF      },
+                              { ".webp",     IFF_WEBP      } };
+
+
+/*---------------------------------------------------------------------*
+ *           Set jpeg quality for pixWrite() and pixWriteMem()         *
+ *---------------------------------------------------------------------*/
+    /* Parameter that controls jpeg quality for high-level calls. */
+static l_int32  var_JPEG_QUALITY = 75;   /* default */
+
+/*!
+ * \brief   l_jpegSetQuality()
+ *
+ * \param[in]    new_quality    1 - 100; 75 is default; 0 defaults to 75
+ * \return       prev           previous quality
+ *
+ * <pre>
+ * Notes:
+ *      (1) This variable is used in pixWriteStream() and pixWriteMem(),
+ *          to control the jpeg quality.  The default is 75.
+ *      (2) It returns the previous quality, so for example:
+ *           l_int32  prev = l_jpegSetQuality(85);  //sets to 85
+ *           pixWriteStream(...);
+ *           l_jpegSetQuality(prev);   // resets to previous value
+ *      (3) On error, logs a message and does not change the variable.
+ */
+l_int32
+l_jpegSetQuality(l_int32  new_quality)
+{
+l_int32  prevq, newq;
+
+    prevq = var_JPEG_QUALITY;
+    newq = (new_quality == 0) ? 75 : new_quality;
+    if (newq < 1 || newq > 100)
+        L_ERROR("invalid jpeg quality; unchanged\n", __func__);
+    else
+        var_JPEG_QUALITY = newq;
+    return prevq;
+}
+
+
+/*----------------------------------------------------------------------*
+ *    Set global variable LeptDebugOK for writing to named temp files   *
+ *----------------------------------------------------------------------*/
+LEPT_DLL l_int32 LeptDebugOK = 0;  /* default value */
+/*!
+ * \brief   setLeptDebugOK()
+ *
+ * \param[in]    allow     TRUE (1) or FALSE (0)
+ * \return       void
+ *
+ * <pre>
+ * Notes:
+ *      (1) This sets or clears the global variable LeptDebugOK, to
+ *          control writing files in a temp directory with names that
+ *          are compiled in.
+ *      (2) The default in the library distribution is 0.  Call with
+ *          %allow = 1 for development and debugging.
+ */
+void
+setLeptDebugOK(l_int32  allow)
+{
+    if (allow != 0) allow = 1;
+    LeptDebugOK = allow;
+}
+
+
+/*---------------------------------------------------------------------*
+ *           Top-level procedures for writing images to file           *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixaWriteFiles()
+ *
+ * \param[in]    rootname
+ * \param[in]    pixa
+ * \param[in]    format  defined in imageio.h; see notes for default
+ * \return  0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Use %format = IFF_DEFAULT to decide the output format
+ *          individually for each pix.
+ * </pre>
+ */
+l_ok
+pixaWriteFiles(const char  *rootname,
+               PIXA        *pixa,
+               l_int32      format)
+{
+char     bigbuf[Bufsize];
+l_int32  i, n, pixformat;
+PIX     *pix;
+
+    if (!rootname)
+        return ERROR_INT("rootname not defined", __func__, 1);
+    if (!pixa)
+        return ERROR_INT("pixa not defined", __func__, 1);
+    if (format < 0 || format == IFF_UNKNOWN ||
+        format >= NumImageFileFormatExtensions)
+        return ERROR_INT("invalid format", __func__, 1);
+
+    n = pixaGetCount(pixa);
+    for (i = 0; i < n; i++) {
+        pix = pixaGetPix(pixa, i, L_CLONE);
+        if (format == IFF_DEFAULT)
+            pixformat = pixChooseOutputFormat(pix);
+        else
+            pixformat = format;
+        snprintf(bigbuf, Bufsize, "%s%03d.%s", rootname, i,
+                 ImageFileFormatExtensions[pixformat]);
+        pixWrite(bigbuf, pix, pixformat);
+        pixDestroy(&pix);
+    }
+
+    return 0;
+}
+
+
+/*!
+ * \brief   pixWriteDebug()
+ *
+ * \param[in]    fname
+ * \param[in]    pix
+ * \param[in]    format  defined in imageio.h
+ * \return  0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Debug version, intended for use in the library when writing
+ *          to files in a temp directory with names that are compiled in.
+ *          This is used instead of pixWrite() for all such library calls.
+ *      (2) The global variable LeptDebugOK defaults to 0, and can be set
+ *          or cleared by the function setLeptDebugOK().
+ * </pre>
+ */
+l_ok
+pixWriteDebug(const char  *fname,
+              PIX         *pix,
+              l_int32      format)
+{
+    if (LeptDebugOK) {
+        return pixWrite(fname, pix, format);
+    } else {
+        L_INFO("write to named temp file %s is disabled\n", __func__, fname);
+        return 0;
+    }
+}
+
+
+/*!
+ * \brief   pixWrite()
+ *
+ * \param[in]    fname
+ * \param[in]    pix
+ * \param[in]    format  defined in imageio.h
+ * \return  0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Open for write using binary mode (with the "b" flag)
+ *          to avoid having Windows automatically translate the NL
+ *          into CRLF, which corrupts image files.  On non-Windows
+ *          systems this flag should be ignored, per ISO C90.
+ *          Thanks to Dave Bryan for pointing this out.
+ *      (2) If the default image format IFF_DEFAULT is requested:
+ *          use the input format if known; otherwise, use a lossless format.
+ *      (3) The default jpeg quality is 75.  For some other value,
+ *          Use l_jpegSetQuality().
+ * </pre>
+ */
+l_ok
+pixWrite(const char  *fname,
+         PIX         *pix,
+         l_int32      format)
+{
+l_int32  ret;
+FILE    *fp;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (!fname)
+        return ERROR_INT("fname not defined", __func__, 1);
+
+    if ((fp = fopenWriteStream(fname, "wb+")) == NULL)
+        return ERROR_INT_1("stream not opened", fname, __func__, 1);
+
+    ret = pixWriteStream(fp, pix, format);
+    fclose(fp);
+    if (ret)
+        return ERROR_INT_1("pix not written to stream", fname, __func__, 1);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixWriteAutoFormat()
+ *
+ * \param[in]    filename
+ * \param[in]    pix
+ * \return  0 if OK; 1 on error
+ */
+l_ok
+pixWriteAutoFormat(const char  *filename,
+                   PIX         *pix)
+{
+l_int32  format;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (!filename)
+        return ERROR_INT("filename not defined", __func__, 1);
+
+    if (pixGetAutoFormat(pix, &format))
+        return ERROR_INT("auto format not returned", __func__, 1);
+    return pixWrite(filename, pix, format);
+}
+
+
+/*!
+ * \brief   pixWriteStream()
+ *
+ * \param[in]    fp file stream
+ * \param[in]    pix
+ * \param[in]    format
+ * \return  0 if OK; 1 on error.
+ */
+l_ok
+pixWriteStream(FILE    *fp,
+               PIX     *pix,
+               l_int32  format)
+{
+    if (!fp)
+        return ERROR_INT("stream not defined", __func__, 1);
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    if (format == IFF_DEFAULT)
+        format = pixChooseOutputFormat(pix);
+
+        /* Use bmp format for testing if library for requested
+         * format for jpeg, png or tiff is not available */
+    changeFormatForMissingLib(&format);
+
+    switch(format)
+    {
+    case IFF_BMP:
+        pixWriteStreamBmp(fp, pix);
+        break;
+
+    case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
+        return pixWriteStreamJpeg(fp, pix, var_JPEG_QUALITY, 0);
+
+    case IFF_PNG:   /* no gamma value stored */
+        return pixWriteStreamPng(fp, pix, 0.0);
+
+    case IFF_TIFF:           /* uncompressed */
+    case IFF_TIFF_PACKBITS:  /* compressed, binary only */
+    case IFF_TIFF_RLE:       /* compressed, binary only */
+    case IFF_TIFF_G3:        /* compressed, binary only */
+    case IFF_TIFF_G4:        /* compressed, binary only */
+    case IFF_TIFF_LZW:       /* compressed, all depths */
+    case IFF_TIFF_ZIP:       /* compressed, all depths */
+    case IFF_TIFF_JPEG:      /* compressed, 8 bpp gray and 32 bpp rgb */
+        return pixWriteStreamTiff(fp, pix, format);
+
+    case IFF_PNM:
+        return pixWriteStreamPnm(fp, pix);
+
+    case IFF_PS:
+        return pixWriteStreamPS(fp, pix, NULL, 0, DefaultScaling);
+
+    case IFF_GIF:
+        return pixWriteStreamGif(fp, pix);
+
+    case IFF_JP2:
+        return pixWriteStreamJp2k(fp, pix, 34, 0, L_JP2_CODEC, 0, 0);
+
+    case IFF_WEBP:
+        return pixWriteStreamWebP(fp, pix, 80, 0);
+
+    case IFF_LPDF:
+        return pixWriteStreamPdf(fp, pix, 0, NULL);
+
+    case IFF_SPIX:
+        return pixWriteStreamSpix(fp, pix);
+
+    default:
+        return ERROR_INT("unknown format", __func__, 1);
+    }
+
+    return 0;
+}
+
+
+/*!
+ * \brief   pixWriteImpliedFormat()
+ *
+ * \param[in]    filename
+ * \param[in]    pix
+ * \param[in]    quality iff JPEG; 1 - 100, 0 for default
+ * \param[in]    progressive iff JPEG; 0 for baseline seq., 1 for progressive
+ * \return  0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This determines the output format from the filename extension.
+ *      (2) The last two args are ignored except for requests for jpeg files.
+ *      (3) The jpeg default quality is 75.
+ * </pre>
+ */
+l_ok
+pixWriteImpliedFormat(const char  *filename,
+                      PIX         *pix,
+                      l_int32      quality,
+                      l_int32      progressive)
+{
+l_int32  format;
+
+    if (!filename)
+        return ERROR_INT("filename not defined", __func__, 1);
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+        /* Determine output format */
+    format = getImpliedFileFormat(filename);
+    if (format == IFF_UNKNOWN) {
+        format = IFF_PNG;
+    } else if (format == IFF_TIFF) {
+        if (pixGetDepth(pix) == 1)
+            format = IFF_TIFF_G4;
+        else
+#ifdef _WIN32
+            format = IFF_TIFF_LZW;  /* poor compression */
+#else
+            format = IFF_TIFF_ZIP;  /* native Windows tools can't handle this */
+#endif  /* _WIN32 */
+    }
+
+    if (format == IFF_JFIF_JPEG) {
+        quality = L_MIN(quality, 100);
+        quality = L_MAX(quality, 0);
+        if (progressive != 0 && progressive != 1) {
+            progressive = 0;
+            L_WARNING("invalid progressive; setting to baseline\n", __func__);
+        }
+        if (quality == 0)
+            quality = 75;
+        pixWriteJpeg (filename, pix, quality, progressive);
+    } else {
+        pixWrite(filename, pix, format);
+    }
+
+    return 0;
+}
+
+
+/*---------------------------------------------------------------------*
+ *          Selection of output format if default is requested         *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixChooseOutputFormat()
+ *
+ * \param[in]    pix
+ * \return  output format, or 0 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This should only be called if the requested format is IFF_DEFAULT.
+ *      (2) If the pix wasn't read from a file, its input format value
+ *          will be IFF_UNKNOWN, and in that case it is written out
+ *          in a compressed but lossless format.
+ * </pre>
+ */
+l_int32
+pixChooseOutputFormat(PIX  *pix)
+{
+l_int32  d, format;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+
+    d = pixGetDepth(pix);
+    format = pixGetInputFormat(pix);
+    if (format == IFF_UNKNOWN) {  /* output lossless */
+        if (d == 1)
+            format = IFF_TIFF_G4;
+        else
+            format = IFF_PNG;
+    }
+
+    return format;
+}
+
+
+/*!
+ * \brief   getImpliedFileFormat()
+ *
+ * \param[in]    filename
+ * \return  output format, or IFF_UNKNOWN on error or invalid extension.
+ *
+ * <pre>
+ * Notes:
+ *      (1) This determines the output file format from the extension
+ *          of the input filename.
+ * </pre>
+ */
+l_int32
+getImpliedFileFormat(const char  *filename)
+{
+char    *extension;
+l_int32  format = IFF_UNKNOWN;
+
+    if (!filename)
+        return ERROR_INT("extension not defined", __func__, IFF_UNKNOWN);
+
+    if (splitPathAtExtension (filename, NULL, &extension))
+        return IFF_UNKNOWN;
+
+    format = getFormatFromExtension(extension);
+    LEPT_FREE(extension);
+    return format;
+}
+
+
+/*!
+ * \brief   getFormatFromExtension()
+ *
+ * \param[in]    extension
+ * \return  output format, or IFF_UNKNOWN on error or invalid extension.
+ *
+ * <pre>
+ * Notes:
+ *      (1) This determines the integer for writing in a format that
+ *          corresponds to the image file type extension.  For example,
+ *          the integer code corresponding to the extension "jpg" is 2;
+ *          it is used to write with jpeg encoding.
+ * </pre>
+ */
+l_int32
+getFormatFromExtension(const char  *extension)
+{
+int      i, numext;
+l_int32  format = IFF_UNKNOWN;
+
+    if (!extension)
+        return ERROR_INT("extension not defined", __func__, IFF_UNKNOWN);
+
+    numext = sizeof(extension_map) / sizeof(extension_map[0]);
+    for (i = 0; i < numext; i++) {
+        if (!strcmp(extension, extension_map[i].extension)) {
+            format = extension_map[i].format;
+            break;
+        }
+    }
+    return format;
+}
+
+
+/*!
+ * \brief   pixGetAutoFormat()
+ *
+ * \param[in]    pix
+ * \param[in]    &format
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) The output formats are restricted to tiff, jpeg and png
+ *          because these are the most commonly used image formats and
+ *          the ones that are typically installed with leptonica.
+ *      (2) This decides what compression to use based on the pix.
+ *          It chooses tiff-g4 if 1 bpp without a colormap, jpeg with
+ *          quality 75 if grayscale, rgb or rgba (where it loses
+ *          the alpha layer), and lossless png for all other situations.
+ * </pre>
+ */
+l_ok
+pixGetAutoFormat(PIX      *pix,
+                 l_int32  *pformat)
+{
+l_int32   d;
+PIXCMAP  *cmap;
+
+    if (!pformat)
+        return ERROR_INT("&format not defined", __func__, 0);
+    *pformat = IFF_UNKNOWN;
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+
+    d = pixGetDepth(pix);
+    cmap = pixGetColormap(pix);
+    if (d == 1 && !cmap) {
+        *pformat = IFF_TIFF_G4;
+    } else if ((d == 8 && !cmap) || d == 24 || d == 32) {
+        *pformat = IFF_JFIF_JPEG;
+    } else {
+        *pformat = IFF_PNG;
+    }
+
+    return 0;
+}
+
+
+/*!
+ * \brief   getFormatExtension()
+ *
+ * \param[in]    format integer
+ * \return  extension string, or NULL if format is out of range
+ *
+ * <pre>
+ * Notes:
+ *      (1) This string is NOT owned by the caller; it is just a pointer
+ *          to a global string.  Do not free it.
+ * </pre>
+ */
+const char *
+getFormatExtension(l_int32  format)
+{
+    if (format < 0 || format >= NumImageFileFormatExtensions)
+        return (const char *)ERROR_PTR("invalid format", __func__, NULL);
+
+    return ImageFileFormatExtensions[format];
+}
+
+
+/*---------------------------------------------------------------------*
+ *                            Write to memory                          *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixWriteMem()
+ *
+ * \param[out]   pdata data of tiff compressed image
+ * \param[out]   psize size of returned data
+ * \param[in]    pix
+ * \param[in]    format  defined in imageio.h
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) On Windows, this will only write tiff and PostScript to memory.
+ *          For other formats, it requires open_memstream(3).
+ *      (2) PostScript output is uncompressed, in hex ascii.
+ *          Most printers support level 2 compression (tiff_g4 for 1 bpp,
+ *          jpeg for 8 and 32 bpp).
+ *      (3) The default jpeg quality is 75.  For some other value,
+ *          Use l_jpegSetQuality().
+ * </pre>
+ */
+l_ok
+pixWriteMem(l_uint8  **pdata,
+            size_t    *psize,
+            PIX       *pix,
+            l_int32    format)
+{
+l_int32  ret;
+
+    if (!pdata)
+        return ERROR_INT("&data not defined", __func__, 1 );
+    if (!psize)
+        return ERROR_INT("&size not defined", __func__, 1 );
+    if (!pix)
+        return ERROR_INT("&pix not defined", __func__, 1 );
+
+    if (format == IFF_DEFAULT)
+        format = pixChooseOutputFormat(pix);
+
+        /* Use bmp format for testing if library for requested
+         * format for jpeg, png or tiff is not available */
+    changeFormatForMissingLib(&format);
+
+    switch(format)
+    {
+    case IFF_BMP:
+        ret = pixWriteMemBmp(pdata, psize, pix);
+        break;
+
+    case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
+        ret = pixWriteMemJpeg(pdata, psize, pix, var_JPEG_QUALITY, 0);
+        break;
+
+    case IFF_PNG:   /* no gamma value stored */
+        ret = pixWriteMemPng(pdata, psize, pix, 0.0);
+        break;
+
+    case IFF_TIFF:           /* uncompressed */
+    case IFF_TIFF_PACKBITS:  /* compressed, binary only */
+    case IFF_TIFF_RLE:       /* compressed, binary only */
+    case IFF_TIFF_G3:        /* compressed, binary only */
+    case IFF_TIFF_G4:        /* compressed, binary only */
+    case IFF_TIFF_LZW:       /* compressed, all depths */
+    case IFF_TIFF_ZIP:       /* compressed, all depths */
+    case IFF_TIFF_JPEG:      /* compressed, 8 bpp gray or 32 bpp rgb */
+        ret = pixWriteMemTiff(pdata, psize, pix, format);
+        break;
+
+    case IFF_PNM:
+        ret = pixWriteMemPnm(pdata, psize, pix);
+        break;
+
+    case IFF_PS:
+        ret = pixWriteMemPS(pdata, psize, pix, NULL, 0, DefaultScaling);
+        break;
+
+    case IFF_GIF:
+        ret = pixWriteMemGif(pdata, psize, pix);
+        break;
+
+    case IFF_JP2:
+        ret = pixWriteMemJp2k(pdata, psize, pix, 34, 0, 0, 0);
+        break;
+
+    case IFF_WEBP:
+        ret = pixWriteMemWebP(pdata, psize, pix, 80, 0);
+        break;
+
+    case IFF_LPDF:
+        ret = pixWriteMemPdf(pdata, psize, pix, 0, NULL);
+        break;
+
+    case IFF_SPIX:
+        ret = pixWriteMemSpix(pdata, psize, pix);
+        break;
+
+    default:
+        return ERROR_INT("unknown format", __func__, 1);
+    }
+
+    return ret;
+}
+
+
+/*---------------------------------------------------------------------*
+ *                      Image display for debugging                    *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   l_fileDisplay()
+ *
+ * \param[in]    fname
+ * \param[in]    x, y  location of display frame on the screen
+ * \param[in]    scale  scale factor (use 0 to skip display)
+ * \return  0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This is a convenient wrapper for displaying image files.
+ *      (2) It does nothing unless LeptDebugOK == TRUE.
+ *      (2) Set %scale = 0 to disable display.
+ *      (3) This downscales 1 bpp to gray.
+ * </pre>
+ */
+l_ok
+l_fileDisplay(const char  *fname,
+              l_int32      x,
+              l_int32      y,
+              l_float32    scale)
+{
+PIX  *pixs, *pixd;
+
+    if (!LeptDebugOK) {
+        L_INFO("displaying files is disabled; "
+               "use setLeptDebugOK(1) to enable\n", __func__);
+        return 0;
+    }
+    if (scale == 0.0)
+        return 0;
+    if (scale < 0.0)
+        return ERROR_INT("invalid scale factor", __func__, 1);
+    if ((pixs = pixRead(fname)) == NULL)
+        return ERROR_INT("pixs not read", __func__, 1);
+
+    if (scale == 1.0) {
+        pixd = pixClone(pixs);
+    } else {
+        if (scale < 1.0 && pixGetDepth(pixs) == 1)
+            pixd = pixScaleToGray(pixs, scale);
+        else
+            pixd = pixScale(pixs, scale, scale);
+    }
+    pixDisplay(pixd, x, y);
+    pixDestroy(&pixs);
+    pixDestroy(&pixd);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixDisplay()
+ *
+ * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
+ * \param[in]    x, y  location of display frame on the screen
+ * \return  0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This is debugging code that displays an image on the screen.
+ *          It uses a static internal variable to number the output files
+ *          written by a single process.  Behavior with a shared library
+ *          may be unpredictable.
+ *      (2) It does nothing unless LeptDebugOK == TRUE.
+ *      (3) It uses these programs to display the image:
+ *             On Unix: xzgv, xli, xv or (for apple) open
+ *             On Windows: i_view or the application currently registered
+ *                         as the default viewer (the browser 'open' action
+ *                         based on the extension of the image file).
+ *          The display program must be on your $PATH variable.  It is
+ *          chosen by setting the global var_DISPLAY_PROG, using
+ *          l_chooseDisplayProg().  Default on Unix is xzgv.
+ *      (4) Images with dimensions larger than MaxDisplayWidth or
+ *          MaxDisplayHeight are downscaled to fit those constraints.
+ *          This is particularly important for displaying 1 bpp images
+ *          with xv, because xv automatically downscales large images
+ *          by subsampling, which looks poor.  For 1 bpp, we use
+ *          scale-to-gray to get decent-looking anti-aliased images.
+ *          In all cases, we write a temporary file to /tmp/lept/disp,
+ *          that is read by the display program.
+ *      (5) The temporary file is written as png if, after initial
+ *          processing for special cases, any of these obtain:
+ *            * pix dimensions are smaller than some thresholds
+ *            * pix depth is less than 8 bpp
+ *            * pix is colormapped
+ *      (6) For spp == 4, we call pixDisplayLayersRGBA() to show 3
+ *          versions of the image: the image with a fully opaque
+ *          alpha, the alpha, and the image as it would appear with
+ *          a white background.
+ *      (7) pixDisplay() can be inactivated at runtime by calling:
+ *               l_chooseDisplayProg(L_DISPLAY_WITH_NONE);
+ * </pre>
+ */
+l_ok
+pixDisplay(PIX     *pixs,
+           l_int32  x,
+           l_int32  y)
+{
+    return pixDisplayWithTitle(pixs, x, y, NULL, 1);
+}
+
+
+/*!
+ * \brief   pixDisplayWithTitle()
+ *
+ * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
+ * \param[in]    x, y  location of display frame
+ * \param[in]    title [optional] on frame; can be NULL;
+ * \param[in]    dispflag 1 to write, else disabled
+ * \return  0 if OK; 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) See notes for pixDisplay().
+ *      (2) This displays the image if dispflag == 1 and the global
+ *          var_DISPLAY_PROG != L_DISPLAY_WITH_NONE; otherwise it punts.
+ * </pre>
+ */
+l_ok
+pixDisplayWithTitle(PIX         *pixs,
+                    l_int32      x,
+                    l_int32      y,
+                    const char  *title,
+                    l_int32      dispflag)
+{
+char           *tempname;
+char            buffer[Bufsize];
+static l_atomic index = 0;  /* caution: not .so safe */
+l_int32         w, h, d, spp, maxheight, opaque, threeviews;
+l_float32       ratw, rath, ratmin;
+PIX            *pix0, *pix1, *pix2;
+PIXCMAP        *cmap;
+#ifndef _WIN32
+l_int32         wt, ht;
+#else
+char           *pathname;
+char            fullpath[_MAX_PATH];
+#endif  /* _WIN32 */
+
+    if (!LeptDebugOK) {
+        L_INFO("displaying images is disabled;\n      "
+               "use setLeptDebugOK(1) to enable\n", __func__);
+        return 0;
+    }
+
+#ifdef OS_IOS /* iOS 11 does not support system() */
+    return ERROR_INT("iOS 11 does not support system()", __func__, 1);
+#endif /* OS_IOS */
+
+    if (dispflag != 1 || var_DISPLAY_PROG == L_DISPLAY_WITH_NONE)
+        return 0;
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+
+#ifndef _WIN32  /* unix */
+    if (var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV &&
+        var_DISPLAY_PROG != L_DISPLAY_WITH_XLI &&
+        var_DISPLAY_PROG != L_DISPLAY_WITH_XV &&
+        var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN)
+        return ERROR_INT("invalid unix program chosen for display",
+                         __func__, 1);
+#else  /* _WIN32 */
+    if (var_DISPLAY_PROG != L_DISPLAY_WITH_IV &&
+        var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN)
+        return ERROR_INT("invalid windows program chosen for display",
+                         __func__, 1);
+#endif  /* _WIN32 */
+
+        /* Display with three views if either spp = 4 or if colormapped
+         * and the alpha component is not fully opaque */
+    opaque = TRUE;
+    if ((cmap = pixGetColormap(pixs)) != NULL)
+        pixcmapIsOpaque(cmap, &opaque);
+    spp = pixGetSpp(pixs);
+    threeviews = (spp == 4 || !opaque) ? TRUE : FALSE;
+
+        /* If colormapped and not opaque, remove the colormap to RGBA */
+    if (!opaque)
+        pix0 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA);
+    else
+        pix0 = pixClone(pixs);
+
+        /* Scale if necessary; this will also remove a colormap */
+    pixGetDimensions(pix0, &w, &h, &d);
+    maxheight = (threeviews) ? MaxDisplayHeight / 3 : MaxDisplayHeight;
+    if (w <= MaxDisplayWidth && h <= maxheight) {
+        if (d == 16)  /* take MSB */
+            pix1 = pixConvert16To8(pix0, L_MS_BYTE);
+        else
+            pix1 = pixClone(pix0);
+    } else {
+        ratw = (l_float32)MaxDisplayWidth / (l_float32)w;
+        rath = (l_float32)maxheight / (l_float32)h;
+        ratmin = L_MIN(ratw, rath);
+        if (ratmin < 0.125 && d == 1)
+            pix1 = pixScaleToGray8(pix0);
+        else if (ratmin < 0.25 && d == 1)
+            pix1 = pixScaleToGray4(pix0);
+        else if (ratmin < 0.33 && d == 1)
+            pix1 = pixScaleToGray3(pix0);
+        else if (ratmin < 0.5 && d == 1)
+            pix1 = pixScaleToGray2(pix0);
+        else
+            pix1 = pixScale(pix0, ratmin, ratmin);
+    }
+    pixDestroy(&pix0);
+    if (!pix1)
+        return ERROR_INT("pix1 not made", __func__, 1);
+
+        /* Generate the three views if required */
+    if (threeviews)
+        pix2 = pixDisplayLayersRGBA(pix1, 0xffffff00, 0);
+    else
+        pix2 = pixClone(pix1);
+
+    if (index == 0) {  /* erase any existing images */
+        lept_rmdir("lept/disp");
+        lept_mkdir("lept/disp");
+    }
+
+    index++;
+    if (pixGetDepth(pix2) < 8 || pixGetColormap(pix2) ||
+        (w < MaxSizeForPng && h < MaxSizeForPng)) {
+        snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.png", index);
+        pixWrite(buffer, pix2, IFF_PNG);
+    } else {
+        snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.jpg", index);
+        pixWrite(buffer, pix2, IFF_JFIF_JPEG);
+    }
+    tempname = genPathname(buffer, NULL);
+
+#ifndef _WIN32
+
+        /* Unix */
+    if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) {
+            /* no way to display title */
+        pixGetDimensions(pix2, &wt, &ht, NULL);
+        snprintf(buffer, Bufsize,
+                 "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10,
+                 x, y, tempname);
+    } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) {
+        if (title) {
+            snprintf(buffer, Bufsize,
+               "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &",
+               x, y, title, tempname);
+        } else {
+            snprintf(buffer, Bufsize,
+               "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &",
+               x, y, tempname);
+        }
+    } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) {
+        if (title) {
+            snprintf(buffer, Bufsize,
+                     "xv -quit -geometry +%d+%d -name \"%s\" %s &",
+                     x, y, title, tempname);
+        } else {
+            snprintf(buffer, Bufsize,
+                     "xv -quit -geometry +%d+%d %s &", x, y, tempname);
+        }
+    } else {  /* L_DISPLAY_WITH_OPEN */
+        snprintf(buffer, Bufsize, "open %s &", tempname);
+    }
+    callSystemDebug(buffer);
+
+#else  /* _WIN32 */
+
+        /* Windows: L_DISPLAY_WITH_IV || L_DISPLAY_WITH_OPEN */
+    pathname = genPathname(tempname, NULL);
+    _fullpath(fullpath, pathname, sizeof(fullpath));
+    if (var_DISPLAY_PROG == L_DISPLAY_WITH_IV) {
+        if (title) {
+            snprintf(buffer, Bufsize,
+                     "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"",
+                     fullpath, x, y, title);
+        } else {
+            snprintf(buffer, Bufsize, "i_view32.exe \"%s\" /pos=(%d,%d)",
+                     fullpath, x, y);
+        }
+    } else {  /* L_DISPLAY_WITH_OPEN */
+        snprintf(buffer, Bufsize, "explorer.exe /open,\"%s\"", fullpath);
+    }
+    callSystemDebug(buffer);
+    LEPT_FREE(pathname);
+
+#endif  /* _WIN32 */
+
+    pixDestroy(&pix1);
+    pixDestroy(&pix2);
+    LEPT_FREE(tempname);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixMakeColorSquare()
+ *
+ * \param[in]    color      in 0xrrggbb00 format
+ * \param[in]    size       in pixels; >= 100; use 0 for default (min size)
+ * \param[in]    addlabel   use 1 to display the color component values
+ * \param[in]    location   of text: L_ADD_ABOVE, etc; ignored if %addlabel == 0
+ * \param[in]    textcolor  of text label; in 0xrrggbb00 format
+ * \return  32 bpp rgb pixd if OK; NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) If %addlabel == 0, %location and %textcolor are ignored.
+ *      (2) To make an array of color squares, use pixDisplayColorArray().
+ * </pre>
+ */
+PIX *
+pixMakeColorSquare(l_uint32  color,
+                   l_int32   size,
+                   l_int32   addlabel,
+                   l_int32   location,
+                   l_uint32  textcolor)
+{
+char     buf[32];
+l_int32  w, rval, gval, bval;
+L_BMF   *bmf;
+PIX     *pix1, *pix2;
+
+    w = (size <= 0) ? 100 : size;
+    if (addlabel && w < 100) {
+        L_WARNING("size too small for label; omitting label\n", __func__);
+        addlabel = 0;
+    }
+
+    if ((pix1 = pixCreate(w, w, 32)) == NULL)
+        return (PIX *)ERROR_PTR("pix1 not madel", __func__, NULL);
+    pixSetAllArbitrary(pix1, color);
+    if (!addlabel)
+        return pix1;
+
+        /* Adding text of color component values */
+    if (location != L_ADD_ABOVE && location != L_ADD_AT_TOP &&
+        location != L_ADD_AT_BOT && location != L_ADD_BELOW) {
+        L_ERROR("invalid location: adding below\n", __func__);
+        location = L_ADD_BELOW;
+    }
+    bmf = bmfCreate(NULL, 4);
+    extractRGBValues(color, &rval, &gval, &bval);
+    snprintf(buf, sizeof(buf), "%d,%d,%d", rval, gval, bval);
+    pix2 = pixAddSingleTextblock(pix1, bmf, buf, textcolor, location, NULL);
+    pixDestroy(&pix1);
+    bmfDestroy(&bmf);
+    return pix2;
+}
+
+
+void
+l_chooseDisplayProg(l_int32  selection)
+{
+    if (selection == L_DISPLAY_WITH_XLI ||
+        selection == L_DISPLAY_WITH_XZGV ||
+        selection == L_DISPLAY_WITH_XV ||
+        selection == L_DISPLAY_WITH_IV ||
+        selection == L_DISPLAY_WITH_OPEN) {
+        var_DISPLAY_PROG = selection;
+    } else {
+        L_ERROR("invalid display program\n", "l_chooseDisplayProg");
+    }
+}
+
+
+/*---------------------------------------------------------------------*
+ *                   Change format for missing lib                     *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   changeFormatForMissingLib()
+ *
+ * \param[in,out]    pformat    addr of requested output image format
+ * \return  void
+ *
+ * <pre>
+ * Notes:
+ *      (1) This is useful for testing functionality when the library for
+ *          the requested output format (jpeg, png or tiff) is not linked.
+ *          In that case, the output format is changed to bmp.
+ * </pre>
+ */
+void
+changeFormatForMissingLib(l_int32  *pformat)
+{
+#if !defined(HAVE_LIBJPEG)
+    if (*pformat == IFF_JFIF_JPEG) {
+        L_WARNING("jpeg library missing; output bmp format\n", __func__);
+        *pformat = IFF_BMP;
+    }
+#endif  /* !defined(HAVE_LIBJPEG) */
+#if !defined(HAVE_LIBPNG)
+    if (*pformat == IFF_PNG) {
+        L_WARNING("png library missing; output bmp format\n", __func__);
+        *pformat = IFF_BMP;
+    }
+#endif  /* !defined(HAVE_LIBPNG) */
+#if !defined(HAVE_LIBTIFF)
+    if (L_FORMAT_IS_TIFF(*pformat)) {
+        L_WARNING("tiff library missing; output bmp format\n", __func__);
+        *pformat = IFF_BMP;
+    }
+#endif  /* !defined(HAVE_LIBTIFF) */
+}
+
+
+/*---------------------------------------------------------------------*
+ *                Deprecated pix output for debugging                  *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixDisplayWrite()
+ *
+ * \param[in]    pix
+ * \param[in]    reduction
+ * \return  1 (error)
+ *
+ * <pre>
+ * Notes:
+ *      As of 1.80, this is a non-functional stub.
+ * </pre>
+ */
+l_ok
+pixDisplayWrite(PIX     *pixs,
+                l_int32  reduction)
+{
+    lept_stderr("\n########################################################\n"
+                "  pixDisplayWrite() was last used in tesseract 3.04,"
+                "  in Feb 2016.  As of 1.80, it is a non-functional stub\n"
+                "########################################################"
+                "\n\n\n");
+    return 1;
+}