mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
2800 lines
101 KiB
C
2800 lines
101 KiB
C
/*====================================================================*
|
||
- Copyright (C) 2001 Leptonica. All rights reserved.
|
||
- This software is distributed in the hope that it will be
|
||
- useful, but with NO WARRANTY OF ANY KIND.
|
||
- No author or distributor accepts responsibility to anyone for the
|
||
- consequences of using this software, or for whether it serves any
|
||
- particular purpose or works at all, unless he or she says so in
|
||
- writing. Everyone is granted permission to copy, modify and
|
||
- redistribute this source code, for commercial or non-commercial
|
||
- purposes, with the following restrictions: (1) the origin of this
|
||
- source code must not be misrepresented; (2) modified versions must
|
||
- be plainly marked as such; and (3) this notice may not be removed
|
||
- or altered from any source or modified source distribution.
|
||
*====================================================================*/
|
||
|
||
/*
|
||
* psio.c
|
||
*
|
||
* |=============================================================|
|
||
* | Important note |
|
||
* |=============================================================|
|
||
* |Some of these functions require libtiff and libjpeg. |
|
||
* |If you do not have both of these libraries, you must set |
|
||
* | #define USE_PSIO 0 |
|
||
* |in environ.h. This will link psiostub.c |
|
||
* |=============================================================|
|
||
*
|
||
* This is a PostScript "device driver" for wrapping images
|
||
* in PostScript. The images can be rendered by a PostScript
|
||
* interpreter for viewing, using evince or gv. They can also be
|
||
* rasterized for printing, using gs or an embedded interpreter
|
||
* in a PostScript printer. And they can be converted to a pdf
|
||
* using gs (ps2pdf).
|
||
*
|
||
* Convert specified files to PS
|
||
* l_int32 convertFilesToPS()
|
||
* l_int32 sarrayConvertFilesToPS()
|
||
* l_int32 convertFilesFittedToPS()
|
||
* l_int32 sarrayConvertFilesFittedToPS()
|
||
* static l_int32 writeImageCompressedToPSFile()
|
||
*
|
||
* Convert mixed text/image files to PS
|
||
* l_int32 convertSegmentedPagesToPS()
|
||
* l_int32 pixWriteSegmentedPageToPS()
|
||
* l_int32 pixWriteMixedToPS()
|
||
* NUMA *sarrayFindMaskAndPagePairings()
|
||
*
|
||
* Convert any image file to PS for embedding
|
||
* l_int32 convertToPSEmbed()
|
||
*
|
||
* For uncompressed images
|
||
* l_int32 pixWritePSEmbed()
|
||
* l_int32 pixWriteStreamPS()
|
||
* char *pixWriteStringPS()
|
||
* void getScaledParametersPS()
|
||
* l_int32 convertByteToHexAscii()
|
||
*
|
||
* For jpeg compressed images
|
||
* l_int32 convertJpegToPSEmbed()
|
||
* l_int32 convertJpegToPS()
|
||
* l_int32 convertJpegToPSString()
|
||
*
|
||
* For tiff g4 compressed images
|
||
* l_int32 convertTiffG4ToPSEmbed()
|
||
* l_int32 convertTiffG4ToPS()
|
||
* l_int32 convertTiffG4ToPSString()
|
||
*
|
||
* For multipage tiff images
|
||
* l_int32 convertTiffMultipageToPS()
|
||
*
|
||
* Write to memory
|
||
* l_int32 pixWriteMemPS()
|
||
*
|
||
* Converting resolution
|
||
* l_int32 getResLetterPage()
|
||
* l_int32 getResA4Page()
|
||
*
|
||
* Utility for encoding and decoding data with ascii85
|
||
* char *encodeAscii85()
|
||
* l_int32 *convertChunkToAscii85()
|
||
* l_uint8 *decodeAscii85()
|
||
*
|
||
* These PostScript converters are used in three different ways:
|
||
*
|
||
* (1) For embedding a PS file in a program like TeX. We must have
|
||
* a bounding box. convertToPSEmbed() handles this for
|
||
* both level 1 and level 2 output, and prog/converttops
|
||
* wraps this in an executable. converttops is a generalization
|
||
* of Thomas Merz's jpeg2ps wrapper, in that it works for
|
||
* all types (formats, depth, colormap) of input images and
|
||
* gives PS output in either compressed or uncompressed format,
|
||
* depending on an input flag.
|
||
*
|
||
* (2) For composing a set of pages with any number of images
|
||
* painted on them, in DCT or G4 compressed format depending
|
||
* on if the image is grayscale/color or binary. Because we
|
||
* append each PS string and specify the scaling and placement
|
||
* explicitly, one must NOT have a bounding box attached to
|
||
* each separate image.
|
||
*
|
||
* (3) For printing a page image or a set of page images, at a
|
||
* resolution that optimally fills the page. Here we use
|
||
* a bounding box and scale the image appropriately.
|
||
*
|
||
* The top-level calls of utilities in category 2, which can compose
|
||
* multiple images on a page, and which generate a PostScript file for
|
||
* printing or display (e.g., conversion to pdf), are:
|
||
* convertFilesToPS()
|
||
* convertFilesFittedToPS()
|
||
* convertSegmentedPagesToPS()
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include "allheaders.h"
|
||
|
||
/* --------------------------------------------*/
|
||
#if USE_PSIO /* defined in environ.h */
|
||
/* --------------------------------------------*/
|
||
|
||
/* Static helper for writing or appending images to an output file */
|
||
static l_int32 writeImageCompressedToPSFile(const char *filein,
|
||
const char *fileout,
|
||
l_int32 format, l_int32 res,
|
||
l_int32 *pfirstfile,
|
||
l_int32 *pindex);
|
||
|
||
static const char *TEMP_G4TIFF_FILE = "/tmp/junk_temp_g4tiff.tif";
|
||
static const char *TEMP_JPEG_FILE = "/tmp/junk_temp_jpeg.jpg";
|
||
|
||
/* MS VC++ can't handle array initialization with static consts ! */
|
||
#define L_BUF_SIZE 512
|
||
|
||
static const l_int32 DEFAULT_PRINTER_RES = 300; /* default printing ppi */
|
||
static const l_int32 MIN_RES = 5;
|
||
static const l_int32 MAX_RES = 3000;
|
||
static const l_int32 MAX_85_LINE_COUNT = 64;
|
||
|
||
/* For computing resolution that fills page to desired amount */
|
||
static const l_int32 LETTER_WIDTH = 612; /* points */
|
||
static const l_int32 LETTER_HEIGHT = 792; /* points */
|
||
static const l_int32 A4_WIDTH = 595; /* points */
|
||
static const l_int32 A4_HEIGHT = 842; /* points */
|
||
static const l_float32 DEFAULT_FILL_FRACTION = 0.95;
|
||
|
||
static const l_uint32 power85[5] = {1,
|
||
85,
|
||
85 * 85,
|
||
85 * 85 * 85,
|
||
85 * 85 * 85 * 85};
|
||
|
||
#ifndef NO_CONSOLE_IO
|
||
#define DEBUG_MIXED_PS 0
|
||
#define DEBUG_JPEG 0
|
||
#define DEBUG_G4 0
|
||
#endif /* ~NO_CONSOLE_IO */
|
||
|
||
/* This should be false for documents that are composited from
|
||
* sequences of painted images, where more than one image can
|
||
* be placed in an arbitrary location on any page.
|
||
* However, for images that are composited, we use special *Embed()
|
||
* functions for writing PostScript with bounding boxes, so they
|
||
* can be embedded in TeX files, e.g. */
|
||
#define PRINT_BOUNDING_BOX 0
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* Convert files in a directory to PS *
|
||
*-------------------------------------------------------------*/
|
||
/*
|
||
* convertFilesToPS()
|
||
*
|
||
* Input: dirin (input directory)
|
||
* substr (<optional> substring filter on filenames; can be NULL)
|
||
* res (typ. 300 or 600 ppi)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This generates a PS file for all image files in a specified
|
||
* directory that contain the substr pattern to be matched.
|
||
* (2) Each image is written to a separate page in the output PS file.
|
||
* (3) All images are written with level 2 compression.
|
||
* If the image is 1 bpp, use G4. Otherwise, use DCT.
|
||
* All colormaps are removed. If the image is neither 1 bpp nor
|
||
* initially jpeg compressed, it is jpeg compressed with
|
||
* quality = 75, which will in general cause some degradation.
|
||
* (4) The resolution is often confusing. It is interpreted
|
||
* as the resolution of the output display device: "If the
|
||
* input image were digitized at 300 ppi, what would it
|
||
* look like when displayed at res ppi." So, for example,
|
||
* if res = 100 ppi, then the display pixels are 3x larger
|
||
* than the 300 ppi pixels, and the image will be rendered
|
||
* 3x larger.
|
||
* (5) The size of the PostScript file is independent of the resolution,
|
||
* because the entire file is encoded. The res parameter just
|
||
* tells the PS decomposer how to render the page. Therefore,
|
||
* for minimum file size without loss of visual information,
|
||
* if the output res is less than 300, you should downscale
|
||
* the image to the output resolution before wrapping in PS.
|
||
* (6) The "canvas" on which the image is rendered, at the given
|
||
* output resolution, is a standard page size (8.5 x 11 in).
|
||
* (7) If the image is jpeg or tiffg4, we use the existing
|
||
* compressed string; otherwise it is necessary to decompress
|
||
* it, remove any existing colormap, and write it out in
|
||
* a temp file in one of these two formats.
|
||
*/
|
||
l_int32
|
||
convertFilesToPS(const char *dirin,
|
||
const char *substr,
|
||
l_int32 res,
|
||
const char *fileout)
|
||
{
|
||
SARRAY *sa;
|
||
|
||
PROCNAME("convertFilesToPS");
|
||
|
||
if (!dirin)
|
||
return ERROR_INT("dirin not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (res <= 0) {
|
||
L_INFO("setting res to 300 ppi", procName);
|
||
res = 300;
|
||
}
|
||
if (res < 10 || res > 4000)
|
||
L_WARNING("res is typically in the range 300-600 ppi", procName);
|
||
|
||
/* Get all filtered and sorted full pathnames. */
|
||
sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0);
|
||
|
||
/* Generate the PS file. */
|
||
sarrayConvertFilesToPS(sa, res, fileout);
|
||
sarrayDestroy(&sa);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* sarrayConvertFilesToPS()
|
||
*
|
||
* Input: sarray (of full path names)
|
||
* res (typ. 300 or 600 ppi)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) See convertFilesToPS()
|
||
*/
|
||
l_int32
|
||
sarrayConvertFilesToPS(SARRAY *sa,
|
||
l_int32 res,
|
||
const char *fileout)
|
||
{
|
||
char *fname;
|
||
l_int32 i, d, nfiles, index, firstfile, format;
|
||
FILE *fp;
|
||
PIX *pix, *pixt;
|
||
|
||
PROCNAME("sarrayConvertFilesToPS");
|
||
|
||
if (!sa)
|
||
return ERROR_INT("sa not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (res <= 0) {
|
||
L_INFO("setting res to 300 ppi", procName);
|
||
res = 300;
|
||
}
|
||
if (res < 10 || res > 4000)
|
||
L_WARNING("res is typically in the range 300-600 ppi", procName);
|
||
|
||
nfiles = sarrayGetCount(sa);
|
||
firstfile = TRUE;
|
||
for (i = 0, index = 0; i < nfiles; i++) {
|
||
fname = sarrayGetString(sa, i, L_NOCOPY);
|
||
if ((fp = fopen(fname, "r")) == NULL)
|
||
continue;
|
||
if(findFileFormat(fp, &format))
|
||
continue;
|
||
fclose(fp);
|
||
|
||
/* Convert to tiffg4 or jpeg if necessary */
|
||
if (format != IFF_JFIF_JPEG && format != IFF_TIFF_G4) {
|
||
if ((pix = pixRead(fname)) == NULL)
|
||
continue;
|
||
d = pixGetDepth(pix);
|
||
if (d == 1) {
|
||
pixWrite(TEMP_G4TIFF_FILE, pix, IFF_TIFF_G4);
|
||
fname = stringNew(TEMP_G4TIFF_FILE);
|
||
format = IFF_TIFF_G4;
|
||
}
|
||
else {
|
||
pixt = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
|
||
pixWrite(TEMP_JPEG_FILE, pixt, IFF_JFIF_JPEG);
|
||
pixDestroy(&pixt);
|
||
fname = stringNew(TEMP_JPEG_FILE);
|
||
format = IFF_JFIF_JPEG;
|
||
}
|
||
pixDestroy(&pix);
|
||
}
|
||
else /* wrap it up as is */
|
||
fname = stringNew(fname);
|
||
|
||
writeImageCompressedToPSFile(fname, fileout, format, res,
|
||
&firstfile, &index);
|
||
FREE(fname);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* convertFilesFittedToPS()
|
||
*
|
||
* Input: dirin (input directory)
|
||
* substr (<optional> substring filter on filenames; can be NULL)
|
||
* xpts, ypts (desired size in printer points; use 0 for default)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This generates a PS file for all files in a specified directory
|
||
* that contain the substr pattern to be matched.
|
||
* (2) Each image is written to a separate page in the output PS file.
|
||
* (3) All images are written with level 2 compression.
|
||
* If the image is 1 bpp, use G4. Otherwise, use DCT.
|
||
* All colormaps are removed. If the image is neither 1 bpp nor
|
||
* initially jpeg compressed, it is jpeg compressed with
|
||
* quality = 75, which will in general cause some degradation.
|
||
* (4) The resolution is internally determined such that the images
|
||
* are rendered, in at least one direction, at 100% of the given
|
||
* size in printer points. Use 0.0 for xpts or ypts to get
|
||
* the default value, which is 612.0 or 792.0, rsp.
|
||
* (5) The size of the PostScript file is independent of the resolution,
|
||
* because the entire file is encoded. The @xpts and @ypts
|
||
* parameter tells the PS decomposer how to render the page.
|
||
* (6) If the image is jpeg or tiffg4, we use the existing
|
||
* compressed string; otherwise it is necessary to decompress
|
||
* it, remove any existing colormap, and write it out in
|
||
* a temp file in one of these two formats.
|
||
*/
|
||
l_int32
|
||
convertFilesFittedToPS(const char *dirin,
|
||
const char *substr,
|
||
l_float32 xpts,
|
||
l_float32 ypts,
|
||
const char *fileout)
|
||
{
|
||
SARRAY *sa;
|
||
|
||
PROCNAME("convertFilesFittedToPS");
|
||
|
||
if (!dirin)
|
||
return ERROR_INT("dirin not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (xpts <= 0.0) {
|
||
L_INFO("setting xpts to 612.0 ppi", procName);
|
||
xpts = 612.0;
|
||
}
|
||
if (ypts <= 0.0) {
|
||
L_INFO("setting ypts to 792.0 ppi", procName);
|
||
ypts = 792.0;
|
||
}
|
||
if (xpts < 100.0 || xpts > 2000.0 || ypts < 100.0 || ypts > 2000.0)
|
||
L_WARNING("xpts,ypts are typically in the range 500-800", procName);
|
||
|
||
/* Get all filtered and sorted full pathnames. */
|
||
sa = getSortedPathnamesInDirectory(dirin, substr, 0, 0);
|
||
|
||
/* Generate the PS file. */
|
||
sarrayConvertFilesFittedToPS(sa, xpts, ypts, fileout);
|
||
sarrayDestroy(&sa);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* sarrayConvertFilesFittedToPS()
|
||
*
|
||
* Input: sarray (of full path names)
|
||
* xpts, ypts (desired size in printer points; use 0 for default)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) See convertFilesFittedToPS()
|
||
*/
|
||
l_int32
|
||
sarrayConvertFilesFittedToPS(SARRAY *sa,
|
||
l_float32 xpts,
|
||
l_float32 ypts,
|
||
const char *fileout)
|
||
{
|
||
char *fname;
|
||
l_int32 i, w, h, d, nfiles, index, firstfile, format, res;
|
||
FILE *fp;
|
||
PIX *pix, *pixt;
|
||
|
||
PROCNAME("sarrayConvertFilesFittedToPS");
|
||
|
||
if (!sa)
|
||
return ERROR_INT("sa not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (xpts <= 0.0) {
|
||
L_INFO("setting xpts to 612.0 ppi", procName);
|
||
xpts = 612.0;
|
||
}
|
||
if (ypts <= 0.0) {
|
||
L_INFO("setting ypts to 792.0 ppi", procName);
|
||
ypts = 792.0;
|
||
}
|
||
if (xpts < 100.0 || xpts > 2000.0 || ypts < 100.0 || ypts > 2000.0)
|
||
L_WARNING("xpts,ypts are typically in the range 500-800", procName);
|
||
|
||
nfiles = sarrayGetCount(sa);
|
||
firstfile = TRUE;
|
||
for (i = 0, index = 0; i < nfiles; i++) {
|
||
fname = sarrayGetString(sa, i, L_NOCOPY);
|
||
if ((fp = fopen(fname, "r")) == NULL)
|
||
continue;
|
||
if(findFileFormat(fp, &format))
|
||
continue;
|
||
pix = pixReadStream(fp, 0);
|
||
fclose(fp);
|
||
if (!pix)
|
||
continue;
|
||
|
||
/* Be sure the entire image is contained in the result */
|
||
pixGetDimensions(pix, &w, &h, &d);
|
||
if (xpts * h < ypts * w)
|
||
res = (l_int32)((l_float32)w * 72.0 / xpts);
|
||
else
|
||
res = (l_int32)((l_float32)h * 72.0 / ypts);
|
||
|
||
/* Convert to tiffg4 or jpeg if necessary */
|
||
if (format != IFF_JFIF_JPEG && format != IFF_TIFF_G4) {
|
||
if (d == 1) {
|
||
pixWrite(TEMP_G4TIFF_FILE, pix, IFF_TIFF_G4);
|
||
fname = stringNew(TEMP_G4TIFF_FILE);
|
||
format = IFF_TIFF_G4;
|
||
}
|
||
else {
|
||
pixt = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
|
||
pixWrite(TEMP_JPEG_FILE, pixt, IFF_JFIF_JPEG);
|
||
pixDestroy(&pixt);
|
||
fname = stringNew(TEMP_JPEG_FILE);
|
||
format = IFF_JFIF_JPEG;
|
||
}
|
||
}
|
||
else /* wrap it up as is */
|
||
fname = stringNew(fname);
|
||
pixDestroy(&pix);
|
||
|
||
writeImageCompressedToPSFile(fname, fileout, format, res,
|
||
&firstfile, &index);
|
||
FREE(fname);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* writeImageCompressedToPSFile()
|
||
*
|
||
* Input: filein (input image file)
|
||
* fileout (output ps file)
|
||
* format (input image file format)
|
||
* res (output printer resolution)
|
||
* &firstfile (<input and return> 1 if the first image;
|
||
* 0 otherwise)
|
||
* &index (<input and return> index of image in output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This assumes the compressed format is either tiffg4 or jpeg.
|
||
* (2) @index is incremented if the page is successfully written.
|
||
*/
|
||
static l_int32
|
||
writeImageCompressedToPSFile(const char *filein,
|
||
const char *fileout,
|
||
l_int32 format,
|
||
l_int32 res,
|
||
l_int32 *pfirstfile,
|
||
l_int32 *pindex)
|
||
{
|
||
l_int32 retval;
|
||
|
||
PROCNAME("writeImageCompressedToPSFile");
|
||
|
||
if (!pfirstfile || !pindex)
|
||
return ERROR_INT("&firstfile and &index not defined", procName, 1);
|
||
|
||
if (format == IFF_JFIF_JPEG) {
|
||
if (*pfirstfile) {
|
||
retval = convertJpegToPS(filein, fileout, "w", 0, 0,
|
||
res, 1.0, *pindex + 1, TRUE);
|
||
if (retval == 0) {
|
||
*pfirstfile = FALSE;
|
||
(*pindex)++;
|
||
}
|
||
}
|
||
else {
|
||
retval = convertJpegToPS(filein, fileout, "a", 0, 0,
|
||
res, 1.0, *pindex + 1, TRUE);
|
||
if (retval == 0)
|
||
(*pindex)++;
|
||
}
|
||
}
|
||
else if (format == IFF_TIFF_G4) {
|
||
if (*pfirstfile) {
|
||
retval = convertTiffG4ToPS(filein, fileout, "w", 0, 0,
|
||
res, 1.0, *pindex + 1, FALSE, TRUE);
|
||
if (retval == 0) {
|
||
*pfirstfile = FALSE;
|
||
(*pindex)++;
|
||
}
|
||
|
||
}
|
||
else {
|
||
retval = convertTiffG4ToPS(filein, fileout, "a", 0, 0,
|
||
res, 1.0, *pindex + 1, FALSE, TRUE);
|
||
if (retval == 0)
|
||
(*pindex)++;
|
||
}
|
||
}
|
||
else
|
||
return ERROR_INT("file format not tiffg4 or jpeg", procName, 1);
|
||
|
||
return retval;
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* Convert mixed text/image files to PS *
|
||
*-------------------------------------------------------------*/
|
||
/*
|
||
* convertSegmentedPagesToPS()
|
||
*
|
||
* Input: pagedir (input page image directory)
|
||
* maskdir (input mask image directory)
|
||
* textscale (scale of text output relative to pixs)
|
||
* imagescale (scale of image output relative to pixs)
|
||
* threshold (for binarization; typ. about 190; 0 for default)
|
||
* numpre (number of characters in name before number)
|
||
* numpost (number of characters in name after number)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This generates a PS file for all page image and mask files in two
|
||
* specified directories that contain the page numbers as
|
||
* specified below. The page images are taken in lexicographic order.
|
||
* Mask images whose numbers match the page images are used to
|
||
* segment the page images. Page imaes without a matching
|
||
* mask image are scaled, thresholded and rendered entirely as text.
|
||
* (2) Each PS page is generated as a compressed representation of
|
||
* the page image, where the part of the image under the mask
|
||
* is suitably scaled and compressed as DCT (i.e., jpeg), and
|
||
* the remaining part of the page is suitably scaled, thresholded,
|
||
* compressed as G4 (i.e., tiff g4), and rendered by painting
|
||
* black through the resulting text mask.
|
||
* (3) The scaling is typically 2x down for the DCT component
|
||
* (@imagescale = 0.5) and 2x up for the G4 component
|
||
* (@textscale = 2.0).
|
||
* (4) The resolution is automatically set to fit to a
|
||
* letter-size (8.5 x 11 inch) page.
|
||
* (5) Both the DCT and the G4 encoding are PostScript level 2.
|
||
* (6) It is assumed that the pa/home/massimo/sources/upp-svn/MyApps/Kindler/main.cpp:33: error: invalid use of ‘class Upp::Pix’
|
||
ge number is contained within
|
||
* the basename (the filename without directory or extension).
|
||
* @numpre is the number of characters in the basename
|
||
* preceeding the actual page numer; @numpost is the number
|
||
* following the page number.
|
||
*/
|
||
l_int32
|
||
convertSegmentedPagesToPS(const char *pagedir,
|
||
const char *maskdir,
|
||
l_float32 textscale,
|
||
l_float32 imagescale,
|
||
l_int32 threshold,
|
||
l_int32 numpre,
|
||
l_int32 numpost,
|
||
const char *fileout)
|
||
{
|
||
char *pagefile, *maskfile;
|
||
l_int32 pageno, i, npages;
|
||
l_int32 pageindex, maskindex;
|
||
NUMA *naindex;
|
||
PIX *pixs, *pixm;
|
||
SARRAY *sapage, *samask;
|
||
|
||
PROCNAME("convertSegmentedPagesToPS");
|
||
|
||
if (!pagedir)
|
||
return ERROR_INT("pagedir not defined", procName, 1);
|
||
if (!maskdir)
|
||
return ERROR_INT("maskdir not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (threshold <= 0) {
|
||
L_INFO("setting threshold to 190", procName);
|
||
threshold = 190;
|
||
}
|
||
|
||
/* Get sorted full pathnames. */
|
||
sapage = getSortedPathnamesInDirectory(pagedir, NULL, 0, 0);
|
||
samask = getSortedPathnamesInDirectory(maskdir, NULL, 0, 0);
|
||
|
||
/* Go through the filenames, locating the page numbers
|
||
* and matching page images with mask images. */
|
||
naindex = sarrayFindMaskAndPagePairings(sapage, samask, numpre,
|
||
numpost, 10000);
|
||
npages = numaGetCount(naindex) / 2;
|
||
|
||
/* Generate the PS file. */
|
||
pageno = 1;
|
||
for (i = 0; i < 2 * npages; i += 2) {
|
||
numaGetIValue(naindex, i, &pageindex);
|
||
numaGetIValue(naindex, i + 1, &maskindex);
|
||
pagefile = sarrayGetString(sapage, pageindex, L_NOCOPY);
|
||
pixs = pixRead(pagefile);
|
||
pixm = NULL;
|
||
if (maskindex != -1) {
|
||
maskfile = sarrayGetString(samask, maskindex, L_NOCOPY);
|
||
pixm = pixRead(maskfile);
|
||
}
|
||
#if DEBUG_MIXED_PS
|
||
fprintf(stderr, "pageindex[%d] = %d, maskindex[%d] = %d\n",
|
||
i, pageindex, i, maskindex);
|
||
fprintf(stderr, " pagefile[%d]: %s\n", i / 2, pagefile);
|
||
if (pixm)
|
||
fprintf(stderr, " maskfile[%d]: %s\n", i / 2, maskfile);
|
||
#endif /* DEBUG_MIXED_PS */
|
||
|
||
pixWriteSegmentedPageToPS(pixs, pixm, textscale, imagescale,
|
||
threshold, pageno, fileout);
|
||
pixDestroy(&pixs);
|
||
pixDestroy(&pixm);
|
||
pageno++;
|
||
}
|
||
|
||
sarrayDestroy(&sapage);
|
||
sarrayDestroy(&samask);
|
||
numaDestroy(&naindex);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* pixWriteSegmentedPageToPS()
|
||
*
|
||
* Input: pixs (grayscale or color; colormap ok)
|
||
* pixm (<optional> 1 bpp segmentation mask over image region)
|
||
* textscale (scale of text output relative to pixs)
|
||
* imagescale (scale of image output relative to pixs)
|
||
* threshold (threshold for binarization; typ. 190)
|
||
* pageno (page number in set; use 1 for new output file)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This generates the PS string for a mixed text/image page,
|
||
* and adds it to an existing file if @pageno > 1.
|
||
* The PS output is determined by fitting the result to
|
||
* a letter-size (8.5 x 11 inch) page.
|
||
* (2) The two images (pixs and pixm) are at the same resolution
|
||
* (typically 300 ppi). They are used to generate two compressed
|
||
* images, pixb and pixc, that are put directly into the output
|
||
* PS file.
|
||
* (3) pixb is the text component. In the PostScript world, we think of
|
||
* it as a mask through which we paint black. It is produced by
|
||
* scaling pixs by @textscale, and thresholding to 1 bpp.
|
||
* (4) pixc is the image component, which is that part of pixs under
|
||
* the mask pixm. It is scaled from pixs by @imagescale.
|
||
* (5) Typical values are textscale = 2.0 and imagescale = 0.5.
|
||
* (6) If pixm == NULL, the page has only text. If it is all black,
|
||
* the page is all image and has no text.
|
||
* (7) This can be used to write a multi-page PS file, by using
|
||
* sequential page numbers with the same output file. It can
|
||
* also be used to write separate PS files for each page,
|
||
* by using different output files with @pageno = 0 or 1.
|
||
*/
|
||
l_int32
|
||
pixWriteSegmentedPageToPS(PIX *pixs,
|
||
PIX *pixm,
|
||
l_float32 textscale,
|
||
l_float32 imagescale,
|
||
l_int32 threshold,
|
||
l_int32 pageno,
|
||
const char *fileout)
|
||
{
|
||
l_int32 alltext, notext, d, ret;
|
||
l_float32 scaleratio;
|
||
PIX *pixmi, *pixt, *pixg, *pixsc, *pixb, *pixc;
|
||
|
||
PROCNAME("pixWriteSegmentedPageToPS");
|
||
|
||
if (!pixs || pixGetDepth(pixs) == 1)
|
||
return ERROR_INT("pixs is 1 bpp or not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (imagescale <= 0.0 || textscale <= 0.0)
|
||
return ERROR_INT("relative scales must be > 0.0", procName, 1);
|
||
|
||
/* Analyze the page. Determine the ratio by which the
|
||
* binary text mask is scaled relative to the image part.
|
||
* If there is no image region (alltext == TRUE), the
|
||
* text mask will be rendered directly to fit the page,
|
||
* and scaleratio = 1.0. */
|
||
alltext = TRUE;
|
||
notext = FALSE;
|
||
scaleratio = 1.0;
|
||
if (pixm) {
|
||
pixZero(pixm, &alltext); /* pixm empty: all text */
|
||
if (alltext)
|
||
pixm = NULL; /* treat it as not existing here */
|
||
else {
|
||
pixmi = pixInvert(NULL, pixm);
|
||
pixZero(pixmi, ¬ext); /* pixm full; no text */
|
||
pixDestroy(&pixmi);
|
||
scaleratio = textscale / imagescale;
|
||
}
|
||
}
|
||
|
||
pixt = pixConvertTo8Or32(pixs, 0, 0);
|
||
|
||
/* Get the binary text mask */
|
||
pixb = NULL;
|
||
if (notext == FALSE) {
|
||
d = pixGetDepth(pixt);
|
||
if (d == 8)
|
||
pixg = pixClone(pixt);
|
||
else /* d == 32 */
|
||
pixg = pixConvertRGBToLuminance(pixt);
|
||
if (pixm) /* clear out the image parts */
|
||
pixSetMasked(pixg, pixm, 255);
|
||
if (textscale == 1.0)
|
||
pixsc = pixClone(pixg);
|
||
else if (textscale >= 0.7)
|
||
pixsc = pixScaleGrayLI(pixg, textscale, textscale);
|
||
else
|
||
pixsc = pixScaleAreaMap(pixg, textscale, textscale);
|
||
pixb = pixThresholdToBinary(pixsc, threshold);
|
||
pixDestroy(&pixg);
|
||
pixDestroy(&pixsc);
|
||
}
|
||
|
||
/* Get the scaled image region */
|
||
pixc = NULL;
|
||
if (pixm) {
|
||
if (imagescale == 1.0)
|
||
pixc = pixClone(pixt);
|
||
else
|
||
pixc = pixScale(pixt, imagescale, imagescale);
|
||
}
|
||
pixDestroy(&pixt);
|
||
|
||
ret = pixWriteMixedToPS(pixb, pixc, scaleratio, pageno, fileout);
|
||
pixDestroy(&pixb);
|
||
pixDestroy(&pixc);
|
||
return ret;
|
||
}
|
||
|
||
|
||
/*
|
||
* pixWriteMixedToPS()
|
||
*
|
||
* Input: pixb (<optionall> 1 bpp "mask"; typically for text)
|
||
* pixc (<optional> 8 or 32 bpp image regions)
|
||
* scale (relative scale factor for rendering pixb
|
||
* relative to pixc; typ. 4.0)
|
||
* pageno (page number in set; use 1 for new output file)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This low level function generates the PS string for a mixed
|
||
* text/image page, and adds it to an existing file if
|
||
* @pageno > 1.
|
||
* (2) The two images (pixb and pixc) are typically generated at the
|
||
* resolution that they will be rendered in the PS file.
|
||
* (3) pixb is the text component. In the PostScript world, we think of
|
||
* it as a mask through which we paint black.
|
||
* (4) pixc is the (typically halftone) image component. It is
|
||
* white in the rest of the page. To minimize the size of the
|
||
* PS file, it should be rendered at a resolution that is at
|
||
* least equal to its actual resolution.
|
||
* (5) @scale gives the ratio of resolution of pixb to pixc.
|
||
* Typical resolutions are: 600 ppi for pixb, 150 ppi for pixc;
|
||
* so @scale = 4.0. If one of the images is not defined,
|
||
* the value of @scale is ignored.
|
||
* (6) We write pixc with DCT compression (jpeg). This is followed
|
||
* by painting the text as black through the mask pixb. If
|
||
* pixc doesn't exist (alltext), we write the text with the
|
||
* PS "image" operator instead of the "imagemask" operator,
|
||
* because ghostscript's ps2pdf is flaky when the latter is used.
|
||
* (7) The actual output resolution is determined by fitting the
|
||
* result to a letter-size (8.5 x 11 inch) page.
|
||
*/
|
||
l_int32
|
||
pixWriteMixedToPS(PIX *pixb,
|
||
PIX *pixc,
|
||
l_float32 scale,
|
||
l_int32 pageno,
|
||
const char *fileout)
|
||
{
|
||
char *tnameb, *tnamec;
|
||
const char *op;
|
||
l_int32 resb, resc, endpage, maskop, ret;
|
||
|
||
PROCNAME("pixWriteMixedToPS");
|
||
|
||
if (!pixb && !pixc)
|
||
return ERROR_INT("pixb and pixc both undefined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
|
||
/* Compute the resolution that fills a letter-size page. */
|
||
if (!pixc)
|
||
resb = getResLetterPage(pixGetWidth(pixb), pixGetHeight(pixb), 0);
|
||
else {
|
||
resc = getResLetterPage(pixGetWidth(pixc), pixGetHeight(pixc), 0);
|
||
if (pixb)
|
||
resb = (l_int32)(scale * resc);
|
||
}
|
||
|
||
/* Write the jpeg image first */
|
||
if (pixc) {
|
||
tnamec = genTempFilename("/tmp", NULL, ".jpg");
|
||
pixWrite(tnamec, pixc, IFF_JFIF_JPEG);
|
||
endpage = (pixb) ? FALSE : TRUE;
|
||
op = (pageno <= 1) ? "w" : "a";
|
||
ret = convertJpegToPS(tnamec, fileout, op, 0, 0, resc, 1.0,
|
||
pageno, endpage);
|
||
FREE(tnamec);
|
||
if (ret)
|
||
return ERROR_INT("jpeg data not written", procName, 1);
|
||
}
|
||
|
||
/* Write the binary data, either directly or, if there is
|
||
* a jpeg image on the page, through the mask. */
|
||
if (pixb) {
|
||
tnameb = genTempFilename("/tmp", NULL, ".tif");
|
||
pixWrite(tnameb, pixb, IFF_TIFF_G4);
|
||
op = (pageno <= 1 && !pixc) ? "w" : "a";
|
||
maskop = (pixc) ? 1 : 0;
|
||
ret = convertTiffG4ToPS(tnameb, fileout, op, 0, 0, resb, 1.0,
|
||
pageno, maskop, 1);
|
||
FREE(tnameb);
|
||
if (ret)
|
||
return ERROR_INT("tiff data not written", procName, 1);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* sarrayFindMaskAndPagePairings()
|
||
*
|
||
* Input: sapage (array of full pathnames for page images)
|
||
* samask (array of full pathnames for mask images)
|
||
* numpre (number of characters in name before number)
|
||
* numpost (number of characters in name after number)
|
||
* maxnum (only consider page numbers up to this value)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) The pages and masks are matched by the located numbers, so
|
||
* their order in @sapage and @samask doesn't matter.
|
||
* (2) It is assumed that the page number is contained within
|
||
* the basename (the filename without directory or extension).
|
||
* @numpre is the number of characters in the basename
|
||
* preceeding the actual page numer; @numpost is the number
|
||
* following the page number.
|
||
* (3) To use a O(n) matching algorithm, the largest page number
|
||
* is found and two internal arrays of this size are created.
|
||
* This maximum is constrained not to exceed @maxsum,
|
||
* to make sure that an unrealistically large number is not
|
||
* accidentally used to determine the array sizes.
|
||
*/
|
||
NUMA *
|
||
sarrayFindMaskAndPagePairings(SARRAY *sapage,
|
||
SARRAY *samask,
|
||
l_int32 numpre,
|
||
l_int32 numpost,
|
||
l_int32 maxnum)
|
||
{
|
||
char *pagename, *maskname;
|
||
l_int32 i, npage, nmask, ipage, imask, num, max, ret;
|
||
l_int32 *arraypage, *arraymask;
|
||
l_float32 fmax;
|
||
NUMA *napage, *namask, *naindex;
|
||
|
||
PROCNAME("sarrayFindMaskAndPagePairings");
|
||
|
||
if (!sapage)
|
||
return (NUMA *)ERROR_PTR("sapage not defined", procName, NULL);
|
||
if (!samask)
|
||
return (NUMA *)ERROR_PTR("samask not defined", procName, NULL);
|
||
|
||
/* First generate two arrays, corresponding to the filename
|
||
* arrays, that contain the page number extracted from each name. */
|
||
npage = sarrayGetCount(sapage);
|
||
nmask = sarrayGetCount(samask);
|
||
napage = numaCreate(npage);
|
||
namask = numaCreate(nmask);
|
||
for (i = 0; i < npage; i++) {
|
||
pagename = sarrayGetString(sapage, i, L_NOCOPY);
|
||
num = extractNumberFromFilename(pagename, numpre, numpost);
|
||
if (num >= 0)
|
||
numaAddNumber(napage, num);
|
||
}
|
||
for (i = 0; i < nmask; i++) {
|
||
maskname = sarrayGetString(samask, i, L_NOCOPY);
|
||
num = extractNumberFromFilename(maskname, numpre, numpost);
|
||
if (num >= 0)
|
||
numaAddNumber(namask, num);
|
||
}
|
||
|
||
/* Generate two new arrays with the page number as the
|
||
* array index and the index of the filename in the sarray
|
||
* as the array content. If there is no file with
|
||
* a page number, the content is -1. */
|
||
numaGetMax(napage, &fmax, NULL);
|
||
max = L_MIN(10000, (l_int32)fmax);
|
||
arraypage = (l_int32 *)CALLOC(max + 1, sizeof(l_int32));
|
||
arraymask = (l_int32 *)CALLOC(max + 1, sizeof(l_int32));
|
||
for (i = 0; i <= max; i++) { /* initialize to -1 */
|
||
arraypage[i] = -1;
|
||
arraymask[i] = -1;
|
||
}
|
||
for (i = 0; i < npage; i++) {
|
||
ret = numaGetIValue(napage, i, &ipage);
|
||
if (ret == 1 || ipage > max) {
|
||
pagename = sarrayGetString(sapage, i, L_NOCOPY);
|
||
L_WARNING_STRING("bad page name: %s", procName, pagename);
|
||
}
|
||
else
|
||
arraypage[ipage] = i;
|
||
}
|
||
for (i = 0; i < nmask; i++) {
|
||
ret = numaGetIValue(namask, i, &imask);
|
||
if (ret == 1 || imask > max) {
|
||
maskname = sarrayGetString(samask, i, L_NOCOPY);
|
||
L_WARNING_STRING("bad mask name = %s", procName, maskname);
|
||
}
|
||
else
|
||
arraymask[imask] = i;
|
||
}
|
||
|
||
|
||
/* Store the result in a single array that holds each
|
||
* pair of page indices. There should be no situation where
|
||
* the mask exists and the page doesn't, so if the page
|
||
* is not found, we don't store anything. */
|
||
naindex = numaCreate(2 * (max + 1));
|
||
for (i = 0; i <= max; i++) {
|
||
ipage = arraypage[i];
|
||
imask = arraymask[i];
|
||
if (ipage == -1) continue;
|
||
numaAddNumber(naindex, ipage);
|
||
numaAddNumber(naindex, imask);
|
||
}
|
||
|
||
numaDestroy(&napage);
|
||
numaDestroy(&namask);
|
||
FREE(arraypage);
|
||
FREE(arraymask);
|
||
return naindex;
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* Convert any image file to PS for embedding *
|
||
*-------------------------------------------------------------*/
|
||
/*
|
||
* convertToPSEmbed()
|
||
*
|
||
* Input: filein (input image file)
|
||
* fileout (output ps file)
|
||
* level (1 - uncompressed, 2 - compressed)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This is a wrapper function that generates a PS file with
|
||
* a bounding box, from any input image file.
|
||
* (2) Colormaps are removed.
|
||
* (3) If the image is not 1 bpp and is not jpeg compressed,
|
||
* and it is to be written as PS with DCT compression
|
||
* (level = 2), it will first be written to file as jpeg with
|
||
* quality = 75. This will cause some degradation in the image.
|
||
* (4) The bounding box is required when a program such as TeX
|
||
* (through epsf) places and rescales the image.
|
||
* (5) The bounding box is sized for fitting the image to an
|
||
* 8.5 x 11.0 inch page.
|
||
*/
|
||
l_int32
|
||
convertToPSEmbed(const char *filein,
|
||
const char *fileout,
|
||
l_int32 level)
|
||
{
|
||
l_int32 d, format;
|
||
FILE *fp;
|
||
PIX *pix, *pixs;
|
||
|
||
PROCNAME("convertToPSEmbed");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
|
||
if (level == 1) {
|
||
pixWritePSEmbed(filein, fileout);
|
||
return 0;
|
||
}
|
||
|
||
/* We must write out level 2 PS */
|
||
if ((fp = fopen(filein, "r")) == NULL)
|
||
return ERROR_INT("filein not found", procName, 1);
|
||
format = findFileFormat(fp);
|
||
fclose(fp);
|
||
|
||
/* Write out directly if in jpeg or tiff g4 formats */
|
||
if (format == IFF_JFIF_JPEG) {
|
||
convertJpegToPSEmbed(filein, fileout);
|
||
return 0;
|
||
}
|
||
else if (format == IFF_TIFF_G4) {
|
||
convertTiffG4ToPSEmbed(filein, fileout);
|
||
return 0;
|
||
}
|
||
|
||
/* Must convert to jpeg or tiff g4 */
|
||
if ((pixs = pixRead(filein)) == NULL)
|
||
return ERROR_INT("image not read from file", procName, 1);
|
||
d = pixGetDepth(pixs);
|
||
if (d == 16)
|
||
pix = pixConvert16To8(pixs, 1);
|
||
else
|
||
pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
|
||
d = pixGetDepth(pix);
|
||
if (d == 1) {
|
||
pixWrite(TEMP_G4TIFF_FILE, pix, IFF_TIFF_G4);
|
||
convertTiffG4ToPSEmbed(TEMP_G4TIFF_FILE, fileout);
|
||
}
|
||
else {
|
||
pixWrite(TEMP_JPEG_FILE, pix, IFF_JFIF_JPEG);
|
||
convertJpegToPSEmbed(TEMP_JPEG_FILE, fileout);
|
||
}
|
||
|
||
pixDestroy(&pix);
|
||
pixDestroy(&pixs);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* For uncompressed images *
|
||
*-------------------------------------------------------------*/
|
||
/*!
|
||
* pixWritePSEmbed()
|
||
*
|
||
* Input: filein (input file)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This is a simple wrapper function that generates an
|
||
* uncompressed PS file, with a bounding box.
|
||
* (2) The bounding box is required when a program such as TeX
|
||
* (through epsf) places and rescales the image.
|
||
* (3) The bounding box is sized for fitting the image to an
|
||
* 8.5 x 11.0 inch page.
|
||
*/
|
||
l_int32
|
||
pixWritePSEmbed(const char *filein,
|
||
const char *fileout)
|
||
{
|
||
l_int32 w, h;
|
||
l_float32 scale;
|
||
FILE *fp;
|
||
PIX *pix;
|
||
|
||
PROCNAME("pixWritePSEmbed");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
|
||
if ((pix = pixRead(filein)) == NULL)
|
||
return ERROR_INT("image not read from file", procName, 1);
|
||
w = pixGetWidth(pix);
|
||
h = pixGetHeight(pix);
|
||
if (w * 11.0 > h * 8.5)
|
||
scale = 8.5 * 300. / (l_float32)w;
|
||
else
|
||
scale = 11.0 * 300. / (l_float32)h;
|
||
|
||
if ((fp = fopen(fileout, "w")) == NULL)
|
||
return ERROR_INT("file not opened for write", procName, 1);
|
||
pixWriteStreamPS(fp, pix, NULL, 0, scale);
|
||
fclose(fp);
|
||
|
||
pixDestroy(&pix);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*!
|
||
* pixWriteStreamPS()
|
||
*
|
||
* Input: stream
|
||
* pix
|
||
* box (<optional>)
|
||
* res (can use 0 for default of 300 ppi)
|
||
* scale (to prevent scaling, use either 1.0 or 0.0)
|
||
* Return: 0 if OK; 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This writes image in PS format, optionally scaled,
|
||
* adjusted for the printer resolution, and with
|
||
* a bounding box.
|
||
* (2) For details on use of parameters, see pixWriteStringPS().
|
||
*/
|
||
l_int32
|
||
pixWriteStreamPS(FILE *fp,
|
||
PIX *pix,
|
||
BOX *box,
|
||
l_int32 res,
|
||
l_float32 scale)
|
||
{
|
||
char *pstring;
|
||
l_int32 length;
|
||
PIX *pixc;
|
||
|
||
PROCNAME("pixWriteStreamPS");
|
||
|
||
if (!fp)
|
||
return (l_int32)ERROR_INT("stream not open", procName, 1);
|
||
if (!pix)
|
||
return (l_int32)ERROR_INT("pix not defined", procName, 1);
|
||
|
||
if ((pixc = pixConvertForPSWrap(pix)) == NULL)
|
||
return (l_int32)ERROR_INT("pixc not made", procName, 1);
|
||
|
||
pstring = pixWriteStringPS(pixc, box, res, scale);
|
||
length = strlen(pstring);
|
||
fwrite(pstring, 1, length, fp);
|
||
FREE(pstring);
|
||
pixDestroy(&pixc);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*!
|
||
* pixWriteStringPS()
|
||
*
|
||
* Input: pix: 1, 2, 4, 8 bpp, with or without cmap; 32 bpp (RGB)
|
||
* box: (a) If box == null, image is placed, optionally scaled,
|
||
* in a standard b.b. at the center of the page.
|
||
* This is to be used when another program like
|
||
* TeX (through epsf) places the image.
|
||
* (b) If box != null, image is placed without a
|
||
* b.b. at the specified page location and with
|
||
* (optional) scaling. This is to be used when
|
||
* you want to specify exactly where (and optionally
|
||
* how big) you want the image to be.
|
||
* Note that all coordinates are in PS convention,
|
||
* with (0,0) at LL corner of the page:
|
||
* (x,y) location of LL corner of image, in mils.
|
||
* (w,h) scaled size, in mils. Use 0 to
|
||
* scale with "scale" and "res" input.
|
||
* res: resolution, in printer ppi. Use 0 for default (300 ppi).
|
||
* scale: scale factor. If no scaling is desired, use
|
||
* either 1.0 or 0.0. Scaling just resets the resolution
|
||
* parameter; the actual scaling is done in the
|
||
* interpreter at rendering time. This is important:
|
||
* it allows you to scale the image up without
|
||
* increasing the file size.
|
||
*
|
||
* Return: ps string if OK, or null on error
|
||
*
|
||
* Notes:
|
||
* (1) OK, this seems a bit complicated, because there are various
|
||
* ways to scale and not to scale. Here's a summary:
|
||
* (2) If you don't want any scaling at all:
|
||
* * if you are using a box:
|
||
* set w = 0, h = 0, and use scale = 1.0; it will print
|
||
* each pixel unscaled at printer resolution
|
||
* * if you are not using a box:
|
||
* set scale = 1.0; it will print at printer resolution
|
||
* (3) If you want the image to be a certain size in inches:
|
||
* * you must use a box and set the box (w,h) in mils
|
||
* (4) If you want the image to be scaled by a scale factor != 1.0:
|
||
* * if you are using a box:
|
||
* set w = 0, h = 0, and use the desired scale factor;
|
||
* the higher the printer resolution, the smaller the
|
||
* image will actually appear.
|
||
* * if you are not using a box:
|
||
* set the desired scale factor; the higher the printer
|
||
* resolution, the smaller the image will actually appear.
|
||
* (5) Another complication is the proliferation of distance units:
|
||
* * The interface distances are in milli-inches.
|
||
* * Three different units are used internally:
|
||
* - pixels (units of 1/res inch)
|
||
* - printer pts (units of 1/72 inch)
|
||
* - inches
|
||
* * Here is a quiz on volume units from a reviewer:
|
||
* How many UK milli-cups in a US kilo-teaspoon?
|
||
* (Hint: 1.0 US cup = 0.75 UK cup + 0.2 US gill;
|
||
* 1.0 US gill = 24.0 US teaspoons)
|
||
*/
|
||
char *
|
||
pixWriteStringPS(PIX *pixs,
|
||
BOX *box,
|
||
l_int32 res,
|
||
l_float32 scale)
|
||
{
|
||
char nib1, nib2;
|
||
char bigbuf[L_BUF_SIZE];
|
||
char *hexdata, *pstring;
|
||
l_uint8 byteval;
|
||
l_int32 i, j, k, d, wpix, hpix;
|
||
l_float32 wpt, hpt, xpt, ypt;
|
||
l_int32 wpl, psbpl, hexbytes, boxflag, sampledepth;
|
||
l_uint32 *line, *data;
|
||
PIX *pix;
|
||
SARRAY *sa;
|
||
|
||
PROCNAME("pixWriteStringPS");
|
||
|
||
if (!pixs)
|
||
return (char *)ERROR_PTR("pix not defined", procName, NULL);
|
||
|
||
d = pixGetDepth(pixs);
|
||
if (d == 2)
|
||
pix = pixConvert2To8(pixs, 0, 85, 170, 255, 0);
|
||
else if (d == 4)
|
||
pix = pixConvert4To8(pixs, 0);
|
||
else if (d == 16)
|
||
pix = pixConvert16To8(pixs, 1);
|
||
else
|
||
pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
|
||
|
||
/* Get the factors by which PS scales and translates, in pts */
|
||
pixGetDimensions(pix, &wpix, &hpix, &d);
|
||
if (!box)
|
||
boxflag = 0; /* no scaling; b.b. at center */
|
||
else
|
||
boxflag = 1; /* no b.b., specify placement and optional scaling */
|
||
getScaledParametersPS(box, wpix, hpix, res, scale, &xpt, &ypt, &wpt, &hpt);
|
||
|
||
if (d == 1)
|
||
sampledepth = 1;
|
||
else /* d == 8 || d == 32 */
|
||
sampledepth = 8;
|
||
|
||
/* Convert image data to hex string */
|
||
wpl = pixGetWpl(pix);
|
||
if (d == 1 || d == 8)
|
||
psbpl = (wpix * d + 7) / 8; /* packed to byte boundary */
|
||
else /* d == 32 */
|
||
psbpl = 3 * wpix; /* packed to byte boundary */
|
||
data = pixGetData(pix);
|
||
hexbytes = 2 * psbpl * hpix; /* size of ps hex array */
|
||
if ((hexdata = (char *)CALLOC(hexbytes + 1, sizeof(char))) == NULL)
|
||
return (char *)ERROR_PTR("hexdata not made", procName, NULL);
|
||
if (d == 1 || d == 8) {
|
||
for (i = 0, k = 0; i < hpix; i++) {
|
||
line = data + i * wpl;
|
||
for (j = 0; j < psbpl; j++) {
|
||
byteval = GET_DATA_BYTE(line, j);
|
||
convertByteToHexAscii(byteval, &nib1, &nib2);
|
||
hexdata[k++] = nib1;
|
||
hexdata[k++] = nib2;
|
||
}
|
||
}
|
||
}
|
||
else { /* d == 32; hexdata bytes packed RGBRGB..., 2 per sample */
|
||
for (i = 0, k = 0; i < hpix; i++) {
|
||
line = data + i * wpl;
|
||
for (j = 0; j < wpix; j++) {
|
||
byteval = GET_DATA_BYTE(line + j, 0); /* red */
|
||
convertByteToHexAscii(byteval, &nib1, &nib2);
|
||
hexdata[k++] = nib1;
|
||
hexdata[k++] = nib2;
|
||
byteval = GET_DATA_BYTE(line + j, 1); /* green */
|
||
convertByteToHexAscii(byteval, &nib1, &nib2);
|
||
hexdata[k++] = nib1;
|
||
hexdata[k++] = nib2;
|
||
byteval = GET_DATA_BYTE(line + j, 2); /* blue */
|
||
convertByteToHexAscii(byteval, &nib1, &nib2);
|
||
hexdata[k++] = nib1;
|
||
hexdata[k++] = nib2;
|
||
}
|
||
}
|
||
}
|
||
hexdata[k] = '\0';
|
||
|
||
if ((sa = sarrayCreate(0)) == NULL)
|
||
return (char *)ERROR_PTR("sa not made", procName, NULL);
|
||
|
||
sarrayAddString(sa, (char *)"%!Adobe-PS", 1);
|
||
if (boxflag == 0) {
|
||
sprintf(bigbuf,
|
||
"%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
|
||
xpt, ypt, xpt + wpt, ypt + hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
}
|
||
else /* boxflag == 1 */
|
||
sarrayAddString(sa, (char *)"gsave", 1);
|
||
|
||
if (d == 1)
|
||
sarrayAddString(sa,
|
||
(char *)"{1 exch sub} settransfer %invert binary", 1);
|
||
|
||
sprintf(bigbuf, "/bpl %d string def %%bpl as a string", psbpl);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f translate %%set image origin in pts", xpt, ypt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f scale %%set image size in pts", wpt, hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf,
|
||
"%d %d %d %%image dimensions in pixels",
|
||
wpix, hpix, sampledepth);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf,
|
||
"[%d %d %d %d %d %d] %%mapping matrix: [wpix 0 0 -hpix 0 hpix]",
|
||
wpix, 0, 0, -hpix, 0, hpix);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
if (boxflag == 0) {
|
||
if (d == 1 || d == 8)
|
||
sarrayAddString(sa,
|
||
(char *)"{currentfile bpl readhexstring pop} image", 1);
|
||
else /* d == 32 */
|
||
sarrayAddString(sa,
|
||
(char *)"{currentfile bpl readhexstring pop} false 3 colorimage",
|
||
1);
|
||
}
|
||
else { /* boxflag == 1 */
|
||
if (d == 1 || d == 8)
|
||
sarrayAddString(sa,
|
||
(char *)"{currentfile bpl readhexstring pop} bind image", 1);
|
||
else /* d == 32 */
|
||
sarrayAddString(sa,
|
||
(char *)"{currentfile bpl readhexstring pop} bind false 3 colorimage",
|
||
1);
|
||
}
|
||
|
||
sarrayAddString(sa, hexdata, 0);
|
||
|
||
if (boxflag == 0)
|
||
sarrayAddString(sa, (char *)"\nshowpage", 1);
|
||
else /* boxflag == 1 */
|
||
sarrayAddString(sa, (char *)"\ngrestore", 1);
|
||
|
||
if ((pstring = sarrayToString(sa, 1)) == NULL)
|
||
return (char *)ERROR_PTR("pstring not made", procName, NULL);
|
||
|
||
sarrayDestroy(&sa);
|
||
pixDestroy(&pix);
|
||
return pstring;
|
||
}
|
||
|
||
|
||
/*!
|
||
* getScaledParametersPS()
|
||
*
|
||
* Input: box (<optional> location of image in mils; with
|
||
* (x,y) being the LL corner)
|
||
* wpix (pix width in pixels)
|
||
* hpix (pix height in pixels)
|
||
* res (of printer; use 0 for default)
|
||
* scale (use 1.0 or 0.0 for no scaling)
|
||
* &xpt (location of llx in pts)
|
||
* &ypt (location of lly in pts)
|
||
* &wpt (image width in pts)
|
||
* &hpt (image height in pts)
|
||
* Return: void (no arg checking)
|
||
*
|
||
* Notes:
|
||
* (1) The image is always scaled, depending on res and scale.
|
||
* (2) If no box, the image is centered on the page.
|
||
* (3) If there is a box, the image is placed within it.
|
||
*/
|
||
void
|
||
getScaledParametersPS(BOX *box,
|
||
l_int32 wpix,
|
||
l_int32 hpix,
|
||
l_int32 res,
|
||
l_float32 scale,
|
||
l_float32 *pxpt,
|
||
l_float32 *pypt,
|
||
l_float32 *pwpt,
|
||
l_float32 *phpt)
|
||
{
|
||
l_int32 bx, by, bw, bh;
|
||
l_float32 winch, hinch, xinch, yinch, fres;
|
||
|
||
PROCNAME("getScaledParametersPS");
|
||
|
||
if (res == 0)
|
||
res = DEFAULT_PRINTER_RES;
|
||
fres = (l_float32)res;
|
||
|
||
/* Allow the PS interpreter to scale the resolution */
|
||
if (scale == 0.0)
|
||
scale = 1.0;
|
||
if (scale != 1.0) {
|
||
fres = (l_float32)res / scale;
|
||
res = (l_int32)fres;
|
||
}
|
||
|
||
/* Limit valid resolution interval */
|
||
if (res < MIN_RES || res > MAX_RES) {
|
||
L_WARNING_INT("res %d out of bounds; using default res; no scaling",
|
||
procName, res);
|
||
res = DEFAULT_PRINTER_RES;
|
||
fres = (l_float32)res;
|
||
}
|
||
|
||
if (!box) { /* center on page */
|
||
winch = (l_float32)wpix / fres;
|
||
hinch = (l_float32)hpix / fres;
|
||
xinch = (8.5 - winch) / 2.;
|
||
yinch = (11.0 - hinch) / 2.;
|
||
}
|
||
else {
|
||
boxGetGeometry(box, &bx, &by, &bw, &bh);
|
||
if (bw == 0)
|
||
winch = (l_float32)wpix / fres;
|
||
else
|
||
winch = (l_float32)bw / 1000.;
|
||
if (bh == 0)
|
||
hinch = (l_float32)hpix / fres;
|
||
else
|
||
hinch = (l_float32)bh / 1000.;
|
||
xinch = (l_float32)bx / 1000.;
|
||
yinch = (l_float32)by / 1000.;
|
||
}
|
||
|
||
if (xinch < 0)
|
||
L_WARNING("left edge < 0.0 inch", procName);
|
||
if (xinch + winch > 8.5)
|
||
L_WARNING("right edge > 8.5 inch", procName);
|
||
if (yinch < 0.0)
|
||
L_WARNING("bottom edge < 0.0 inch", procName);
|
||
if (yinch + hinch > 11.0)
|
||
L_WARNING("top edge > 11.0 inch", procName);
|
||
|
||
*pwpt = 72. * winch;
|
||
*phpt = 72. * hinch;
|
||
*pxpt = 72. * xinch;
|
||
*pypt = 72. * yinch;
|
||
return;
|
||
}
|
||
|
||
|
||
/*!
|
||
* convertByteToHexAscii()
|
||
*
|
||
* Input: byteval (input byte)
|
||
* &nib1, &nib2 (<return> two hex ascii characters)
|
||
* Return: void
|
||
*/
|
||
void
|
||
convertByteToHexAscii(l_uint8 byteval,
|
||
char *pnib1,
|
||
char *pnib2)
|
||
{
|
||
l_uint8 nib;
|
||
|
||
nib = byteval >> 4;
|
||
if (nib < 10)
|
||
*pnib1 = '0' + nib;
|
||
else
|
||
*pnib1 = 'a' + (nib - 10);
|
||
nib = byteval & 0xf;
|
||
if (nib < 10)
|
||
*pnib2 = '0' + nib;
|
||
else
|
||
*pnib2 = 'a' + (nib - 10);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* For jpeg compressed images *
|
||
*-------------------------------------------------------------*/
|
||
/*!
|
||
* convertJpegToPSEmbed()
|
||
*
|
||
* Input: filein (input jpeg file)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This function takes a jpeg file as input and generates a DCT
|
||
* compressed, ascii85 encoded PS file, with a bounding box.
|
||
* (2) The bounding box is required when a program such as TeX
|
||
* (through epsf) places and rescales the image.
|
||
* (3) The bounding box is sized for fitting the image to an
|
||
* 8.5 x 11.0 inch page.
|
||
*/
|
||
l_int32
|
||
convertJpegToPSEmbed(const char *filein,
|
||
const char *fileout)
|
||
{
|
||
char *pstring, *outstr;
|
||
char *data85; /* ascii85 encoded file */
|
||
char bigbuf[512];
|
||
l_uint8 *bindata; /* binary encoded jpeg data (entire file) */
|
||
l_int32 bps, w, h, spp;
|
||
l_int32 nbinbytes, psbytes, nbytes85, totbytes;
|
||
l_float32 xpt, ypt, wpt, hpt;
|
||
SARRAY *sa;
|
||
|
||
PROCNAME("convertJpegToPSEmbed");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
|
||
/* The returned jpeg data in memory is the entire jpeg file,
|
||
* which starts with ffd8 and ends with ffd9 */
|
||
if (extractJpegDataFromFile(filein, &bindata, &nbinbytes,
|
||
&w, &h, &bps, &spp))
|
||
return ERROR_INT("bindata not extracted from file", procName, 1);
|
||
|
||
/* Convert entire jpeg file of encoded DCT data to ascii85 */
|
||
data85 = encodeAscii85(bindata, nbinbytes, &nbytes85);
|
||
FREE(bindata);
|
||
if (!data85)
|
||
return ERROR_INT("data85 not made", procName, 1);
|
||
|
||
/* Scale for 20 pt boundary and otherwise full filling
|
||
* in one direction on 8.5 x 11 inch device */
|
||
xpt = 20.0;
|
||
ypt = 20.0;
|
||
if (w * 11.0 > h * 8.5) {
|
||
wpt = 572.0; /* 612 - 2 * 20 */
|
||
hpt = wpt * (l_float32)h / (l_float32)w;
|
||
}
|
||
else {
|
||
hpt = 752.0; /* 792 - 2 * 20 */
|
||
wpt = hpt * (l_float32)w / (l_float32)h;
|
||
}
|
||
|
||
/* -------- Generate PostScript output -------- */
|
||
if ((sa = sarrayCreate(50)) == NULL)
|
||
return ERROR_INT("sa not made", procName, 1);
|
||
|
||
sarrayAddString(sa, (char *)"%!PS-Adobe-3.0", 1);
|
||
sarrayAddString(sa, (char *)"%%Creator: leptonica", 1);
|
||
sprintf(bigbuf, "%%%%Title: %s", filein);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf,
|
||
"%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
|
||
xpt, ypt, xpt + wpt, ypt + hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)"%%DocumentData: Clean7Bit", 1);
|
||
sarrayAddString(sa, (char *)"%%LanguageLevel: 2", 1);
|
||
sarrayAddString(sa, (char *)"%%EndComments", 1);
|
||
sarrayAddString(sa, (char *)"%%Page: 1 1", 1);
|
||
|
||
sarrayAddString(sa, (char *)"save", 1);
|
||
sarrayAddString(sa,
|
||
(char *)"/RawData currentfile /ASCII85Decode filter def", 1);
|
||
sarrayAddString(sa, (char *)"/Data RawData << >> /DCTDecode filter def", 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f translate %%set image origin in pts", xpt, ypt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f scale %%set image size in pts", wpt, hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
if (spp == 1)
|
||
sarrayAddString(sa, (char *)"/DeviceGray setcolorspace", 1);
|
||
else if (spp == 3)
|
||
sarrayAddString(sa, (char *)"/DeviceRGB setcolorspace", 1);
|
||
else /*spp == 4 */
|
||
sarrayAddString(sa, (char *)"/DeviceCMYK setcolorspace", 1);
|
||
|
||
sarrayAddString(sa, (char *)"{ << /ImageType 1", 1);
|
||
sprintf(bigbuf, " /Width %d", w);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /Height %d", h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)" /DataSource Data", 1);
|
||
sprintf(bigbuf, " /BitsPerComponent %d", bps);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
if (spp == 1)
|
||
sarrayAddString(sa, (char *)" /Decode [0 1]", 1);
|
||
else if (spp == 3)
|
||
sarrayAddString(sa, (char *)" /Decode [0 1 0 1 0 1]", 1);
|
||
else /* spp == 4 */
|
||
sarrayAddString(sa, (char *)" /Decode [0 1 0 1 0 1 0 1]", 1);
|
||
|
||
sarrayAddString(sa, (char *)" >> image", 1);
|
||
sarrayAddString(sa, (char *)" Data closefile", 1);
|
||
sarrayAddString(sa, (char *)" RawData flushfile", 1);
|
||
sarrayAddString(sa, (char *)" showpage", 1);
|
||
sarrayAddString(sa, (char *)" restore", 1);
|
||
sarrayAddString(sa, (char *)"} exec", 1);
|
||
|
||
if ((pstring = sarrayToString(sa, 1)) == NULL)
|
||
return ERROR_INT("pstring not made", procName, 1);
|
||
sarrayDestroy(&sa);
|
||
psbytes = strlen(pstring);
|
||
|
||
/* Add the ascii85 data */
|
||
totbytes = psbytes + nbytes85;
|
||
if ((outstr = (char *)CALLOC(totbytes + 4, sizeof(char))) == NULL)
|
||
return ERROR_INT("outstr not made", procName, 1);
|
||
memcpy(outstr, pstring, psbytes);
|
||
memcpy(outstr + psbytes, data85, nbytes85);
|
||
FREE(pstring);
|
||
FREE(data85);
|
||
|
||
if (arrayWrite(fileout, "w", outstr, totbytes))
|
||
return ERROR_INT("ps string not written to file", procName, 1);
|
||
FREE(outstr);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*!
|
||
* convertJpegToPS()
|
||
*
|
||
* Input: filein (input jpeg file)
|
||
* fileout (output ps file)
|
||
* operation ("w" for write; "a" for append)
|
||
* x, y (location of LL corner of image, in pixels, relative
|
||
* to the PostScript origin (0,0) at the LL corner
|
||
* of the page)
|
||
* res (resolution of the input image, in ppi; use 0 for default)
|
||
* scale (scaling by printer; use 0.0 or 1.0 for no scaling)
|
||
* pageno (page number; must start with 1; you can use 0
|
||
* if there is only one page.)
|
||
* endpage (boolean: TRUE if the last image to be
|
||
* added to the page; FALSE otherwise)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This is simpler to use than pixWriteStringPS(), and
|
||
* it outputs in level 2 PS as compressed DCT (overlaid
|
||
* with ascii85 encoding).
|
||
* (2) An output file can contain multiple pages, each with
|
||
* multiple images. The arguments to convertJpegToPS()
|
||
* allow you to control placement of jpeg images on multiple
|
||
* pages within a PostScript file.
|
||
* (3) For the first image written to a file, use "w", which
|
||
* opens for write and clears the file. For all subsequent
|
||
* images written to that file, use "a".
|
||
* (4) The (x, y) parameters give the LL corner of the image
|
||
* relative to the LL corner of the page. They are in
|
||
* units of pixels if scale = 1.0. If you use (e.g.)
|
||
* scale = 2.0, the image is placed at (2x, 2y) on the page,
|
||
* and the image dimensions are also doubled.
|
||
* (5) Display vs printed resolution:
|
||
* * If your display is 75 ppi and your image was created
|
||
* at a resolution of 300 ppi, you can get the image
|
||
* to print at the same size as it appears on your display
|
||
* by either setting scale = 4.0 or by setting res = 75.
|
||
* Both tell the printer to make a 4x enlarged image.
|
||
* * If your image is generated at 150 ppi and you use scale = 1,
|
||
* it will be rendered such that 150 pixels correspond
|
||
* to 72 pts (1 inch on the printer). This function does
|
||
* the conversion from pixels (with or without scaling) to
|
||
* pts, which are the units that the printer uses.
|
||
* * The printer will choose its own resolution to use
|
||
* in rendering the image, which will not affect the size
|
||
* of the rendered image. That is because the output
|
||
* PostScript file describes the geometry in terms of pts,
|
||
* which are defined to be 1/72 inch. The printer will
|
||
* only see the size of the image in pts, through the
|
||
* scale and translate parameters and the affine
|
||
* transform (the ImageMatrix) of the image.
|
||
* (6) To render multiple images on the same page, set
|
||
* endpage = FALSE for each image until you get to the
|
||
* last, for which you set endpage = TRUE. This causes the
|
||
* "showpage" command to be invoked. Showpage outputs
|
||
* the entire page and clears the raster buffer for the
|
||
* next page to be added. Without a "showpage",
|
||
* subsequent images from the next page will overlay those
|
||
* previously put down.
|
||
* (7) For multiple pages, increment the page number, starting
|
||
* with page 1. This allows PostScript (and PDF) to build
|
||
* a page directory, which viewers use for navigation.
|
||
*/
|
||
l_int32
|
||
convertJpegToPS(const char *filein,
|
||
const char *fileout,
|
||
const char *operation,
|
||
l_int32 x,
|
||
l_int32 y,
|
||
l_int32 res,
|
||
l_float32 scale,
|
||
l_int32 pageno,
|
||
l_int32 endpage)
|
||
{
|
||
char *outstr;
|
||
l_int32 nbytes;
|
||
|
||
PROCNAME("convertJpegToPS");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (strcmp(operation, "w") && strcmp(operation, "a"))
|
||
return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1);
|
||
|
||
if (convertJpegToPSString(filein, &outstr, &nbytes, x, y, res, scale,
|
||
pageno, endpage))
|
||
return ERROR_INT("ps string not made", procName, 1);
|
||
|
||
if (arrayWrite(fileout, operation, outstr, nbytes))
|
||
return ERROR_INT("ps string not written to file", procName, 1);
|
||
|
||
FREE(outstr);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*!
|
||
* convertJpegToPSString()
|
||
*
|
||
* Generates PS string in jpeg format from jpeg file
|
||
*
|
||
* Input: filein (input jpeg file)
|
||
* &poutstr (<return> PS string)
|
||
* &nbytes (<return> number of bytes in PS string)
|
||
* x, y (location of LL corner of image, in pixels, relative
|
||
* to the PostScript origin (0,0) at the LL corner
|
||
* of the page)
|
||
* res (resolution of the input image, in ppi; use 0 for default)
|
||
* scale (scaling by printer; use 0.0 or 1.0 for no scaling)
|
||
* pageno (page number; must start with 1; you can use 0
|
||
* if there is only one page.)
|
||
* endpage (boolean: TRUE if the last image to be
|
||
* added to the page; FALSE otherwise)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) The returned PS character array is binary string, not a
|
||
* null-terminated ascii C string. It has null bytes
|
||
* embedded in it!
|
||
*
|
||
* Usage: See convertJpegToPS()
|
||
*/
|
||
l_int32
|
||
convertJpegToPSString(const char *filein,
|
||
char **poutstr,
|
||
l_int32 *pnbytes,
|
||
l_int32 x,
|
||
l_int32 y,
|
||
l_int32 res,
|
||
l_float32 scale,
|
||
l_int32 pageno,
|
||
l_int32 endpage)
|
||
{
|
||
char *pstring, *outstr;
|
||
char *data85; /* ascii85 encoded file */
|
||
char bigbuf[L_BUF_SIZE];
|
||
l_uint8 *bindata; /* binary encoded jpeg data (entire file) */
|
||
l_int32 bps, w, h, spp;
|
||
l_int32 nbinbytes, psbytes, nbytes85, totbytes;
|
||
l_float32 xpt, ypt, wpt, hpt;
|
||
SARRAY *sa;
|
||
|
||
PROCNAME("convertJpegToPSString");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!poutstr)
|
||
return ERROR_INT("&outstr not defined", procName, 1);
|
||
if (!pnbytes)
|
||
return ERROR_INT("&nbytes not defined", procName, 1);
|
||
*poutstr = NULL;
|
||
|
||
/* The returned jpeg data in memory is the entire jpeg file,
|
||
* which starts with ffd8 and ends with ffd9 */
|
||
if (extractJpegDataFromFile(filein, &bindata, &nbinbytes,
|
||
&w, &h, &bps, &spp))
|
||
return ERROR_INT("bindata not extracted from file", procName, 1);
|
||
|
||
/* Convert entire jpeg file of encoded DCT data to ascii85 */
|
||
data85 = encodeAscii85(bindata, nbinbytes, &nbytes85);
|
||
FREE(bindata);
|
||
if (!data85)
|
||
return ERROR_INT("data85 not made", procName, 1);
|
||
|
||
#if DEBUG_JPEG
|
||
fprintf(stderr, "w = %d, h = %d, bps = %d, spp = %d\n", w, h, bps, spp);
|
||
fprintf(stderr, "nbinbytes = %d, nbytes85 = %d, ratio = %5.3f\n",
|
||
nbinbytes, nbytes85, (l_float32)nbytes85 / (l_float32)nbinbytes);
|
||
#endif /* DEBUG_JPEG */
|
||
|
||
/* Get scaled location in pts */
|
||
if (scale == 0.0)
|
||
scale = 1.0;
|
||
if (res == 0)
|
||
res = DEFAULT_PRINTER_RES;
|
||
xpt = scale * x * 72. / res;
|
||
ypt = scale * y * 72. / res;
|
||
wpt = scale * w * 72. / res;
|
||
hpt = scale * h * 72. / res;
|
||
|
||
if (pageno == 0)
|
||
pageno = 1;
|
||
|
||
#if DEBUG_JPEG
|
||
fprintf(stderr, "xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n",
|
||
xpt, ypt, wpt, hpt);
|
||
#endif /* DEBUG_JPEG */
|
||
|
||
/* -------- Generate PostScript output -------- */
|
||
if ((sa = sarrayCreate(50)) == NULL)
|
||
return ERROR_INT("sa not made", procName, 1);
|
||
|
||
sarrayAddString(sa, (char *)"%!PS-Adobe-3.0", 1);
|
||
sarrayAddString(sa, (char *)"%%Creator: leptonica", 1);
|
||
sprintf(bigbuf, "%%%%Title: %s", filein);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
#if PRINT_BOUNDING_BOX
|
||
sprintf(bigbuf,
|
||
"%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
|
||
xpt, ypt, xpt + wpt, ypt + hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
#endif /* PRINT_BOUNDING_BOX */
|
||
|
||
sarrayAddString(sa, (char *)"%%DocumentData: Clean7Bit", 1);
|
||
sarrayAddString(sa, (char *)"%%LanguageLevel: 2", 1);
|
||
sarrayAddString(sa, (char *)"%%EndComments", 1);
|
||
sprintf(bigbuf, "%%%%Page: %d %d", pageno, pageno);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sarrayAddString(sa, (char *)"save", 1);
|
||
sarrayAddString(sa,
|
||
(char *)"/RawData currentfile /ASCII85Decode filter def", 1);
|
||
sarrayAddString(sa, (char *)"/Data RawData << >> /DCTDecode filter def", 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f translate %%set image origin in pts", xpt, ypt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f scale %%set image size in pts", wpt, hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
if (spp == 1)
|
||
sarrayAddString(sa, (char *)"/DeviceGray setcolorspace", 1);
|
||
else if (spp == 3)
|
||
sarrayAddString(sa, (char *)"/DeviceRGB setcolorspace", 1);
|
||
else /*spp == 4 */
|
||
sarrayAddString(sa, (char *)"/DeviceCMYK setcolorspace", 1);
|
||
|
||
sarrayAddString(sa, (char *)"{ << /ImageType 1", 1);
|
||
sprintf(bigbuf, " /Width %d", w);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /Height %d", h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)" /DataSource Data", 1);
|
||
sprintf(bigbuf, " /BitsPerComponent %d", bps);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
if (spp == 1)
|
||
sarrayAddString(sa, (char *)" /Decode [0 1]", 1);
|
||
else if (spp == 3)
|
||
sarrayAddString(sa, (char *)" /Decode [0 1 0 1 0 1]", 1);
|
||
else /* spp == 4 */
|
||
sarrayAddString(sa, (char *)" /Decode [0 1 0 1 0 1 0 1]", 1);
|
||
|
||
sarrayAddString(sa, (char *)" >> image", 1);
|
||
sarrayAddString(sa, (char *)" Data closefile", 1);
|
||
sarrayAddString(sa, (char *)" RawData flushfile", 1);
|
||
if (endpage == TRUE)
|
||
sarrayAddString(sa, (char *)" showpage", 1);
|
||
sarrayAddString(sa, (char *)" restore", 1);
|
||
sarrayAddString(sa, (char *)"} exec", 1);
|
||
|
||
if ((pstring = sarrayToString(sa, 1)) == NULL)
|
||
return ERROR_INT("pstring not made", procName, 1);
|
||
psbytes = strlen(pstring);
|
||
|
||
/* Add the ascii85 data */
|
||
totbytes = psbytes + nbytes85;
|
||
*pnbytes = totbytes;
|
||
if ((outstr = (char *)CALLOC(totbytes + 4, sizeof(char))) == NULL)
|
||
return ERROR_INT("outstr not made", procName, 1);
|
||
*poutstr = outstr;
|
||
memcpy(outstr, pstring, psbytes);
|
||
memcpy(outstr + psbytes, data85, nbytes85);
|
||
|
||
sarrayDestroy(&sa);
|
||
FREE(data85);
|
||
FREE(pstring);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* For tiff g4 compressed images *
|
||
*-------------------------------------------------------------*/
|
||
/*!
|
||
* convertTiffG4ToPSEmbed()
|
||
*
|
||
* Input: filein (input jpeg file)
|
||
* fileout (output ps file)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This function takes a g4 compressed tif file as input and
|
||
* generates a g4 compressed, ascii85 encoded PS file, with
|
||
* a bounding box.
|
||
* (2) The bounding box is required when a program such as TeX
|
||
* (through epsf) places and rescales the image.
|
||
* (3) The bounding box is sized for fitting the image to an
|
||
* 8.5 x 11.0 inch page.
|
||
* (4) We paint this through a mask, over whatever is below.
|
||
*/
|
||
l_int32
|
||
convertTiffG4ToPSEmbed(const char *filein,
|
||
const char *fileout)
|
||
{
|
||
char *pstring, *pstring2, *outstr;
|
||
char *data85; /* ascii85 encoded ccitt g4 data */
|
||
char bigbuf[512];
|
||
l_uint8 *bindata; /* binary encoded ccitt g4 data */
|
||
l_int32 minisblack; /* TRUE or FALSE */
|
||
l_int32 w, h;
|
||
l_int32 nbinbytes, nbytes85, psbytes, psbytes2, totbytes;
|
||
l_float32 xpt, ypt, wpt, hpt;
|
||
SARRAY *sa, *sa2;
|
||
|
||
PROCNAME("convertTiffG4ToPSEmbed");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
|
||
/* The returned ccitt g4 data in memory is the block of
|
||
* bytes in the tiff file, starting after 8 bytes and
|
||
* ending before the directory. */
|
||
if (extractTiffG4DataFromFile(filein, &bindata, &nbinbytes,
|
||
&w, &h, &minisblack))
|
||
return ERROR_INT("bindata not extracted from file", procName, 1);
|
||
|
||
/* Convert the ccittg4 encoded data to ascii85 */
|
||
data85 = encodeAscii85(bindata, nbinbytes, &nbytes85);
|
||
FREE(bindata);
|
||
if (!data85)
|
||
return ERROR_INT("data85 not made", procName, 1);
|
||
|
||
/* Scale for 20 pt boundary and otherwise full filling
|
||
* in one direction on 8.5 x 11 inch device */
|
||
xpt = 20.0;
|
||
ypt = 20.0;
|
||
if (w * 11.0 > h * 8.5) {
|
||
wpt = 572.0; /* 612 - 2 * 20 */
|
||
hpt = wpt * (l_float32)h / (l_float32)w;
|
||
}
|
||
else {
|
||
hpt = 752.0; /* 792 - 2 * 20 */
|
||
wpt = hpt * (l_float32)w / (l_float32)h;
|
||
}
|
||
|
||
/* -------- Generate PostScript output -------- */
|
||
if ((sa = sarrayCreate(50)) == NULL)
|
||
return ERROR_INT("sa not made", procName, 1);
|
||
|
||
sarrayAddString(sa, (char *)"%!PS-Adobe-3.0", 1);
|
||
sarrayAddString(sa, (char *)"%%Creator: leptonica", 1);
|
||
sprintf(bigbuf, "%%%%Title: %s", filein);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)"%%DocumentData: Clean7Bit", 1);
|
||
sprintf(bigbuf,
|
||
"%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
|
||
xpt, ypt, xpt + wpt, ypt + hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sarrayAddString(sa, (char *)"%%LanguageLevel: 2", 1);
|
||
sarrayAddString(sa, (char *)"%%EndComments", 1);
|
||
sarrayAddString(sa, (char *)"%%Page: 1 1", 1);
|
||
|
||
sarrayAddString(sa, (char *)"save", 1);
|
||
sarrayAddString(sa, (char *)"100 dict begin", 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f translate %%set image origin in pts", xpt, ypt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f scale %%set image size in pts", wpt, hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sarrayAddString(sa, (char *)"/DeviceGray setcolorspace", 1);
|
||
|
||
sarrayAddString(sa, (char *)"{", 1);
|
||
sarrayAddString(sa,
|
||
(char *)" /RawData currentfile /ASCII85Decode filter def", 1);
|
||
sarrayAddString(sa, (char *)" << ", 1);
|
||
sarrayAddString(sa, (char *)" /ImageType 1", 1);
|
||
sprintf(bigbuf, " /Width %d", w);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /Height %d", h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)" /BitsPerComponent 1", 1);
|
||
sarrayAddString(sa, (char *)" /Interpolate true", 1);
|
||
if (minisblack)
|
||
sarrayAddString(sa, (char *)" /Decode [1 0]", 1);
|
||
else /* miniswhite; typical for 1 bpp */
|
||
sarrayAddString(sa, (char *)" /Decode [0 1]", 1);
|
||
sarrayAddString(sa, (char *)" /DataSource RawData", 1);
|
||
sarrayAddString(sa, (char *)" <<", 1);
|
||
sarrayAddString(sa, (char *)" /K -1", 1);
|
||
sprintf(bigbuf, " /Columns %d", w);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /Rows %d", h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)" >> /CCITTFaxDecode filter", 1);
|
||
sarrayAddString(sa, (char *)" >> imagemask", 1);
|
||
sarrayAddString(sa, (char *)" RawData flushfile", 1);
|
||
sarrayAddString(sa, (char *)" showpage", 1);
|
||
sarrayAddString(sa, (char *)"}", 1);
|
||
|
||
sarrayAddString(sa, (char *)"%%BeginData:", 1);
|
||
sarrayAddString(sa, (char *)"exec", 1);
|
||
|
||
if ((pstring = sarrayToString(sa, 1)) == NULL)
|
||
return ERROR_INT("pstring not made", procName, 1);
|
||
psbytes = strlen(pstring);
|
||
sarrayDestroy(&sa);
|
||
|
||
/* Concat the trailing data */
|
||
sa2 = sarrayCreate(10);
|
||
sarrayAddString(sa2, (char *)"%%EndData", 1);
|
||
sarrayAddString(sa2, (char *)"end", 1);
|
||
sarrayAddString(sa2, (char *)"restore", 1);
|
||
if ((pstring2 = sarrayToString(sa2, 1)) == NULL)
|
||
return ERROR_INT("pstring2 not made", procName, 1);
|
||
psbytes2 = strlen(pstring2);
|
||
sarrayDestroy(&sa2);
|
||
|
||
/* Add the ascii85 data */
|
||
totbytes = psbytes + psbytes2 + nbytes85;
|
||
if ((outstr = (char *)CALLOC(totbytes + 4, sizeof(char))) == NULL)
|
||
return ERROR_INT("outstr not made", procName, 1);
|
||
memcpy(outstr, pstring, psbytes);
|
||
memcpy(outstr + psbytes, data85, nbytes85);
|
||
memcpy(outstr + psbytes + nbytes85, pstring2, psbytes2);
|
||
FREE(data85);
|
||
FREE(pstring);
|
||
FREE(pstring2);
|
||
|
||
if (arrayWrite(fileout, "w", outstr, totbytes))
|
||
return ERROR_INT("ps string not written to file", procName, 1);
|
||
FREE(outstr);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*!
|
||
* convertTiffG4ToPS()
|
||
*
|
||
* Input: filein (input tiff g4 file)
|
||
* fileout (output ps file)
|
||
* operation ("w" for write; "a" for append)
|
||
* x, y (location of LL corner of image, in pixels, relative
|
||
* to the PostScript origin (0,0) at the LL corner
|
||
* of the page)
|
||
* res (resolution of the input image, in ppi; typ. values
|
||
* are 300 and 600; use 0 for automatic determination
|
||
* based on image size)
|
||
* scale (scaling by printer; use 0.0 or 1.0 for no scaling)
|
||
* pageno (page number; must start with 1; you can use 0
|
||
* if there is only one page.)
|
||
* mask (boolean: use TRUE if just painting through fg;
|
||
* FALSE if painting both fg and bg.
|
||
* endpage (boolean: use TRUE if the last image to be
|
||
* added to the page; FALSE otherwise)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) See the usage comments in convertJpegToPS(), some of
|
||
* which are repeated here.
|
||
* (2) This is a wrapper for tiff g4. The PostScript that
|
||
* is generated is expanded by about 5/4 (due to the
|
||
* ascii85 encoding. If you convert to pdf (ps2pdf), the
|
||
* ascii85 decoder is automatically invoked, so that the
|
||
* pdf wrapped g4 file is essentially the same size as
|
||
* the original g4 file. It's useful to have the PS
|
||
* file ascii85 encoded, because many printers will not
|
||
* print binary PS files.
|
||
* (3) For the first image written to a file, use "w", which
|
||
* opens for write and clears the file. For all subsequent
|
||
* images written to that file, use "a".
|
||
* (4) To render multiple images on the same page, set
|
||
* endpage = FALSE for each image until you get to the
|
||
* last, for which you set endpage = TRUE. This causes the
|
||
* "showpage" command to be invoked. Showpage outputs
|
||
* the entire page and clears the raster buffer for the
|
||
* next page to be added. Without a "showpage",
|
||
* subsequent images from the next page will overlay those
|
||
* previously put down.
|
||
* (5) For multiple images to the same page, where you are writing
|
||
* both jpeg and tiff-g4, you have two options:
|
||
* (a) write the g4 first, as either image (mask == false)
|
||
* or imagemask (mask == true), and then write the
|
||
* jpeg over it.
|
||
* (b) write the jpeg first and as the last item, write
|
||
* the g4 as an imagemask (mask == true), to paint
|
||
* through the foreground only.
|
||
* We have this flexibility with the tiff-g4 because it is 1 bpp.
|
||
* (6) For multiple pages, increment the page number, starting
|
||
* with page 1. This allows PostScript (and PDF) to build
|
||
* a page directory, which viewers use for navigation.
|
||
*/
|
||
l_int32
|
||
convertTiffG4ToPS(const char *filein,
|
||
const char *fileout,
|
||
const char *operation,
|
||
l_int32 x,
|
||
l_int32 y,
|
||
l_int32 res,
|
||
l_float32 scale,
|
||
l_int32 pageno,
|
||
l_int32 mask,
|
||
l_int32 endpage)
|
||
{
|
||
char *outstr;
|
||
l_int32 nbytes;
|
||
|
||
PROCNAME("convertTiffG4ToPS");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
if (strcmp(operation, "w") && strcmp(operation, "a"))
|
||
return ERROR_INT("operation must be \"w\" or \"a\"", procName, 1);
|
||
|
||
if (convertTiffG4ToPSString(filein, &outstr, &nbytes, x, y, res, scale,
|
||
pageno, mask, endpage))
|
||
return ERROR_INT("ps string not made", procName, 1);
|
||
|
||
if (arrayWrite(fileout, operation, outstr, nbytes))
|
||
return ERROR_INT("ps string not written to file", procName, 1);
|
||
|
||
FREE(outstr);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*!
|
||
* convertTiffG4ToPSString()
|
||
*
|
||
* Input: filein (input tiff g4 file)
|
||
* &poutstr (<return> PS string)
|
||
* &nbytes (<return> number of bytes in PS string)
|
||
* x, y (location of LL corner of image, in pixels, relative
|
||
* to the PostScript origin (0,0) at the LL corner
|
||
* of the page)
|
||
* res (resolution of the input image, in ppi; typ. values
|
||
* are 300 and 600; use 0 for automatic determination
|
||
* based on image size)
|
||
* scale (scaling by printer; use 0.0 or 1.0 for no scaling)
|
||
* pageno (page number; must start with 1; you can use 0
|
||
* if there is only one page.)
|
||
* mask (boolean: use TRUE if just painting through fg;
|
||
* FALSE if painting both fg and bg.
|
||
* endpage (boolean: use TRUE if the last image to be
|
||
* added to the page; FALSE otherwise)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) Generates PS string in G4 compressed tiff format from G4 tiff file.
|
||
* (2) The returned PS character array is binary string, not a
|
||
* null-terminated C string. It has null bytes embedded in it!
|
||
* (3) For usage, see convertTiffG4ToPS().
|
||
*/
|
||
l_int32
|
||
convertTiffG4ToPSString(const char *filein,
|
||
char **poutstr,
|
||
l_int32 *pnbytes,
|
||
l_int32 x,
|
||
l_int32 y,
|
||
l_int32 res,
|
||
l_float32 scale,
|
||
l_int32 pageno,
|
||
l_int32 mask,
|
||
l_int32 endpage)
|
||
{
|
||
char *pstring, *pstring2, *outstr;
|
||
char *data85; /* ascii85 encoded ccitt g4 data */
|
||
char bigbuf[L_BUF_SIZE];
|
||
l_uint8 *bindata; /* binary encoded ccitt g4 data */
|
||
l_int32 minisblack; /* TRUE or FALSE */
|
||
l_int32 w, h;
|
||
l_int32 nbinbytes, nbytes85, psbytes, psbytes2, totbytes;
|
||
l_float32 xpt, ypt, wpt, hpt;
|
||
SARRAY *sa, *sa2;
|
||
|
||
PROCNAME("convertTiffG4ToPSString");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!poutstr)
|
||
return ERROR_INT("&outstr not defined", procName, 1);
|
||
if (!pnbytes)
|
||
return ERROR_INT("&nbytes not defined", procName, 1);
|
||
*poutstr = NULL;
|
||
|
||
/* The returned ccitt g4 data in memory is the block of
|
||
* bytes in the tiff file, starting after 8 bytes and
|
||
* ending before the directory. */
|
||
if (extractTiffG4DataFromFile(filein, &bindata, &nbinbytes,
|
||
&w, &h, &minisblack))
|
||
return ERROR_INT("bindata not extracted from file", procName, 1);
|
||
|
||
#if DEBUG_G4
|
||
/* arrayWrite("junkarray", "w", bindata, nbinbytes); */
|
||
fprintf(stderr, "nbinbytes = %d, w = %d, h = %d, minisblack = %d\n",
|
||
nbinbytes, w, h, minisblack);
|
||
#endif /* DEBUG_G4 */
|
||
|
||
/* Convert the ccittg4 encoded data to ascii85 */
|
||
data85 = encodeAscii85(bindata, nbinbytes, &nbytes85);
|
||
FREE(bindata);
|
||
if (!data85)
|
||
return ERROR_INT("data85 not made", procName, 1);
|
||
|
||
/* Get scaled location in pts */
|
||
if (scale == 0.0)
|
||
scale = 1.0;
|
||
if (res == 0) {
|
||
if (h <= 3300)
|
||
res = 300;
|
||
else
|
||
res = 600;
|
||
}
|
||
xpt = scale * x * 72. / res;
|
||
ypt = scale * y * 72. / res;
|
||
wpt = scale * w * 72. / res;
|
||
hpt = scale * h * 72. / res;
|
||
|
||
#if DEBUG_G4
|
||
fprintf(stderr, "xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n",
|
||
xpt, ypt, wpt, hpt);
|
||
#endif /* DEBUG_G4 */
|
||
|
||
/* -------- generate PostScript output -------- */
|
||
if ((sa = sarrayCreate(50)) == NULL)
|
||
return ERROR_INT("sa not made", procName, 1);
|
||
|
||
sarrayAddString(sa, (char *)"%!PS-Adobe-3.0", 1);
|
||
sarrayAddString(sa, (char *)"%%Creator: leptonica", 1);
|
||
sprintf(bigbuf, "%%%%Title: %s", filein);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)"%%DocumentData: Clean7Bit", 1);
|
||
|
||
#if PRINT_BOUNDING_BOX
|
||
sprintf(bigbuf,
|
||
"%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
|
||
xpt, ypt, xpt + wpt, ypt + hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
#endif /* PRINT_BOUNDING_BOX */
|
||
|
||
sarrayAddString(sa, (char *)"%%LanguageLevel: 2", 1);
|
||
sarrayAddString(sa, (char *)"%%EndComments", 1);
|
||
sprintf(bigbuf, "%%%%Page: %d %d", pageno, pageno);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sarrayAddString(sa, (char *)"save", 1);
|
||
sarrayAddString(sa, (char *)"100 dict begin", 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f translate %%set image origin in pts", xpt, ypt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sprintf(bigbuf,
|
||
"%7.2f %7.2f scale %%set image size in pts", wpt, hpt);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
|
||
sarrayAddString(sa, (char *)"/DeviceGray setcolorspace", 1);
|
||
|
||
sarrayAddString(sa, (char *)"{", 1);
|
||
sarrayAddString(sa,
|
||
(char *)" /RawData currentfile /ASCII85Decode filter def", 1);
|
||
sarrayAddString(sa, (char *)" << ", 1);
|
||
sarrayAddString(sa, (char *)" /ImageType 1", 1);
|
||
sprintf(bigbuf, " /Width %d", w);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /Height %d", h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)" /BitsPerComponent 1", 1);
|
||
sarrayAddString(sa, (char *)" /Interpolate true", 1);
|
||
if (minisblack)
|
||
sarrayAddString(sa, (char *)" /Decode [1 0]", 1);
|
||
else /* miniswhite; typical for 1 bpp */
|
||
sarrayAddString(sa, (char *)" /Decode [0 1]", 1);
|
||
sarrayAddString(sa, (char *)" /DataSource RawData", 1);
|
||
sarrayAddString(sa, (char *)" <<", 1);
|
||
sarrayAddString(sa, (char *)" /K -1", 1);
|
||
sprintf(bigbuf, " /Columns %d", w);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sprintf(bigbuf, " /Rows %d", h);
|
||
sarrayAddString(sa, bigbuf, 1);
|
||
sarrayAddString(sa, (char *)" >> /CCITTFaxDecode filter", 1);
|
||
if (mask == TRUE) /* just paint through the fg */
|
||
sarrayAddString(sa, (char *)" >> imagemask", 1);
|
||
else /* paint full image */
|
||
sarrayAddString(sa, (char *)" >> image", 1);
|
||
sarrayAddString(sa, (char *)" RawData flushfile", 1);
|
||
if (endpage == TRUE)
|
||
sarrayAddString(sa, (char *)" showpage", 1);
|
||
sarrayAddString(sa, (char *)"}", 1);
|
||
|
||
sarrayAddString(sa, (char *)"%%BeginData:", 1);
|
||
sarrayAddString(sa, (char *)"exec", 1);
|
||
|
||
if ((pstring = sarrayToString(sa, 1)) == NULL)
|
||
return ERROR_INT("pstring not made", procName, 1);
|
||
psbytes = strlen(pstring);
|
||
|
||
/* Concat the trailing data */
|
||
sa2 = sarrayCreate(10);
|
||
sarrayAddString(sa2, (char *)"%%EndData", 1);
|
||
sarrayAddString(sa2, (char *)"end", 1);
|
||
sarrayAddString(sa2, (char *)"restore", 1);
|
||
if ((pstring2 = sarrayToString(sa2, 1)) == NULL)
|
||
return ERROR_INT("pstring2 not made", procName, 1);
|
||
psbytes2 = strlen(pstring2);
|
||
|
||
/* Add the ascii85 data */
|
||
totbytes = psbytes + psbytes2 + nbytes85;
|
||
*pnbytes = totbytes;
|
||
if ((outstr = (char *)CALLOC(totbytes + 4, sizeof(char))) == NULL)
|
||
return ERROR_INT("outstr not made", procName, 1);
|
||
*poutstr = outstr;
|
||
memcpy(outstr, pstring, psbytes);
|
||
memcpy(outstr + psbytes, data85, nbytes85);
|
||
memcpy(outstr + psbytes + nbytes85, pstring2, psbytes2);
|
||
|
||
sarrayDestroy(&sa);
|
||
sarrayDestroy(&sa2);
|
||
FREE(data85);
|
||
FREE(pstring);
|
||
FREE(pstring2);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* For tiff multipage files *
|
||
*-------------------------------------------------------------*/
|
||
/*!
|
||
* convertTiffMultipageToPS()
|
||
*
|
||
* Input: filein (input tiff multipage file)
|
||
* fileout (output ps file)
|
||
* tempfile (<optional> for temporary g4 tiffs;
|
||
* use NULL for default)
|
||
* factor (for filling 8.5 x 11 inch page;
|
||
* use 0.0 for DEFAULT_FILL_FRACTION)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) This converts a multipage tiff file of binary page images
|
||
* into a ccitt g4 compressed PS file.
|
||
* (2) If the images are generated from a standard resolution fax,
|
||
* the vertical resolution is doubled to give a normal-looking
|
||
* aspect ratio.
|
||
*/
|
||
l_int32
|
||
convertTiffMultipageToPS(const char *filein,
|
||
const char *fileout,
|
||
const char *tempfile,
|
||
l_float32 fillfract)
|
||
{
|
||
const char tempdefault[] = "/tmp/junk_temp_g4.tif";
|
||
const char *tempname;
|
||
l_int32 i, npages, w, h, istiff;
|
||
l_float32 scale;
|
||
PIX *pix, *pixs;
|
||
FILE *fp;
|
||
|
||
PROCNAME("convertTiffMultipageToPS");
|
||
|
||
if (!filein)
|
||
return ERROR_INT("filein not defined", procName, 1);
|
||
if (!fileout)
|
||
return ERROR_INT("fileout not defined", procName, 1);
|
||
|
||
if ((fp = fopen(filein, "r")) == NULL)
|
||
return ERROR_INT("file not found", procName, 1);
|
||
istiff = fileFormatIsTiff(fp);
|
||
if (!istiff) {
|
||
fclose(fp);
|
||
return ERROR_INT("file not tiff format", procName, 1);
|
||
}
|
||
tiffGetCount(fp, &npages);
|
||
fclose(fp);
|
||
|
||
if (tempfile)
|
||
tempname = tempfile;
|
||
else
|
||
tempname = tempdefault;
|
||
|
||
if (fillfract == 0.0)
|
||
fillfract = DEFAULT_FILL_FRACTION;
|
||
|
||
for (i = 0; i < npages; i++) {
|
||
if ((pix = pixReadTiff(filein, i)) == NULL)
|
||
return ERROR_INT("pix not made", procName, 1);
|
||
|
||
w = pixGetWidth(pix);
|
||
h = pixGetHeight(pix);
|
||
if (w == 1728 && h < w) /* it's a std res fax */
|
||
pixs = pixScale(pix, 1.0, 2.0);
|
||
else
|
||
pixs = pixClone(pix);
|
||
|
||
pixWrite(tempname, pixs, IFF_TIFF_G4);
|
||
scale = L_MIN(fillfract * 2550 / w, fillfract * 3300 / h);
|
||
if (i == 0)
|
||
convertTiffG4ToPS(tempname, fileout, "w", 0, 0, 300, scale,
|
||
i + 1, FALSE, TRUE);
|
||
else
|
||
convertTiffG4ToPS(tempname, fileout, "a", 0, 0, 300, scale,
|
||
i + 1, FALSE, TRUE);
|
||
pixDestroy(&pix);
|
||
pixDestroy(&pixs);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*---------------------------------------------------------------------*
|
||
* Write to memory *
|
||
*---------------------------------------------------------------------*/
|
||
/*!
|
||
* pixWriteMemPS()
|
||
*
|
||
* Input: &data (<return> data of tiff compressed image)
|
||
* &size (<return> size of returned data)
|
||
* pix
|
||
* box (<optional>)
|
||
* res (can use 0 for default of 300 ppi)
|
||
* scale (to prevent scaling, use either 1.0 or 0.0)
|
||
* Return: 0 if OK, 1 on error
|
||
*
|
||
* Notes:
|
||
* (1) See pixWriteStringPS() for usage.
|
||
* (2) This is just a wrapper for pixWriteStringPS(), which
|
||
* writes uncompressed image data to memory.
|
||
*/
|
||
l_int32
|
||
pixWriteMemPS(l_uint8 **pdata,
|
||
size_t *psize,
|
||
PIX *pix,
|
||
BOX *box,
|
||
l_int32 res,
|
||
l_float32 scale)
|
||
{
|
||
PROCNAME("pixWriteMemPS");
|
||
|
||
if (!pdata)
|
||
return ERROR_INT("&data not defined", procName, 1 );
|
||
if (!psize)
|
||
return ERROR_INT("&size not defined", procName, 1 );
|
||
if (!pix)
|
||
return ERROR_INT("&pix not defined", procName, 1 );
|
||
|
||
*pdata = (l_uint8 *)pixWriteStringPS(pix, box, res, scale);
|
||
*psize = strlen((char *)(*pdata));
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* Converting resolution *
|
||
*-------------------------------------------------------------*/
|
||
/*!
|
||
* getResLetterPage()
|
||
*
|
||
* Input: w (image width, pixels)
|
||
* h (image height, pixels)
|
||
* fillfract (fraction in linear dimension of full page, not
|
||
* to be exceeded; use 0 for default)
|
||
* Return: 0 if OK, 1 on error
|
||
*/
|
||
l_int32
|
||
getResLetterPage(l_int32 w,
|
||
l_int32 h,
|
||
l_float32 fillfract)
|
||
{
|
||
l_int32 resw, resh, res;
|
||
|
||
if (fillfract == 0.0)
|
||
fillfract = DEFAULT_FILL_FRACTION;
|
||
resw = (l_int32)((w * 72.) / (LETTER_WIDTH * fillfract));
|
||
resh = (l_int32)((h * 72.) / (LETTER_HEIGHT * fillfract));
|
||
res = L_MAX(resw, resh);
|
||
return res;
|
||
}
|
||
|
||
|
||
/*!
|
||
* getResA4Page()
|
||
*
|
||
* Input: w (image width, pixels)
|
||
* h (image height, pixels)
|
||
* fillfract (fraction in linear dimension of full page, not
|
||
* to be exceeded; use 0 for default)
|
||
* Return: 0 if OK, 1 on error
|
||
*/
|
||
l_int32
|
||
getResA4Page(l_int32 w,
|
||
l_int32 h,
|
||
l_float32 fillfract)
|
||
{
|
||
l_int32 resw, resh, res;
|
||
|
||
if (fillfract == 0.0)
|
||
fillfract = DEFAULT_FILL_FRACTION;
|
||
resw = (l_int32)((w * 72.) / (A4_WIDTH * fillfract));
|
||
resh = (l_int32)((h * 72.) / (A4_HEIGHT * fillfract));
|
||
res = L_MAX(resw, resh);
|
||
return res;
|
||
}
|
||
|
||
|
||
|
||
/*-------------------------------------------------------------*
|
||
* Utility for encoding and decoding data with ascii85 *
|
||
*-------------------------------------------------------------*/
|
||
/*!
|
||
* encodeAscii85()
|
||
*
|
||
* Input: inarray (input data)
|
||
* insize (number of bytes in input array)
|
||
* &outsize (<return> number of bytes in output char array)
|
||
* Return: chara (with 64 characters + \n in each line)
|
||
*
|
||
* Notes:
|
||
* (1) Ghostscript has a stack break if the last line of
|
||
* data only has a '>', so we avoid the problem by
|
||
* always putting '~>' on the last line.
|
||
*/
|
||
char *
|
||
encodeAscii85(l_uint8 *inarray,
|
||
l_int32 insize,
|
||
l_int32 *poutsize)
|
||
{
|
||
char *chara;
|
||
char *outbuf;
|
||
l_int32 maxsize, i, index, outindex, linecount, nbout, eof;
|
||
|
||
PROCNAME("encodeAscii85");
|
||
|
||
if (!inarray)
|
||
return (char *)ERROR_PTR("inarray not defined", procName, NULL);
|
||
|
||
/* Accumulate results in chara */
|
||
maxsize = (l_int32)(80. + (insize * 5. / 4.) *
|
||
(1. + 2. / MAX_85_LINE_COUNT));
|
||
if ((chara = (char *)CALLOC(maxsize, sizeof(char))) == NULL)
|
||
return (char *)ERROR_PTR("chara not made", procName, NULL);
|
||
|
||
if ((outbuf = (char *)CALLOC(8, sizeof(char))) == NULL)
|
||
return (char *)ERROR_PTR("outbuf not made", procName, NULL);
|
||
|
||
linecount = 0;
|
||
index = 0;
|
||
outindex = 0;
|
||
while (1) {
|
||
eof = convertChunkToAscii85(inarray, insize, &index, outbuf, &nbout);
|
||
for (i = 0; i < nbout; i++) {
|
||
chara[outindex++] = outbuf[i];
|
||
linecount++;
|
||
if (linecount >= MAX_85_LINE_COUNT) {
|
||
chara[outindex++] = '\n';
|
||
linecount = 0;
|
||
}
|
||
}
|
||
if (eof == TRUE) {
|
||
if (linecount != 0)
|
||
chara[outindex++] = '\n';
|
||
chara[outindex++] = '~';
|
||
chara[outindex++] = '>';
|
||
chara[outindex++] = '\n';
|
||
break;
|
||
}
|
||
}
|
||
|
||
FREE(outbuf);
|
||
*poutsize = outindex;
|
||
return chara;
|
||
}
|
||
|
||
|
||
/*!
|
||
* convertChunkToAscii85()
|
||
*
|
||
* Input: inarray (input data)
|
||
* insize (number of bytes in input array)
|
||
* &index (use and <return> -- ptr)
|
||
* outbuf (holds 8 ascii chars; we use no more than 7)
|
||
* &nbsout (<return> number of bytes written to outbuf)
|
||
* Return: boolean for eof (0 if more data, 1 if end of file)
|
||
*
|
||
* Notes:
|
||
* (1) Attempts to read 4 bytes and write 5.
|
||
* (2) Writes 1 byte if the value is 0; writes 2 extra bytes if EOF.
|
||
*/
|
||
l_int32
|
||
convertChunkToAscii85(l_uint8 *inarray,
|
||
l_int32 insize,
|
||
l_int32 *pindex,
|
||
char *outbuf,
|
||
l_int32 *pnbout)
|
||
{
|
||
l_uint8 inbyte;
|
||
l_uint32 inword, val;
|
||
l_int32 eof, index, nread, nbout, i;
|
||
|
||
eof = FALSE;
|
||
index = *pindex;
|
||
nread = L_MIN(4, (insize - index));
|
||
if (insize == index + nread)
|
||
eof = TRUE;
|
||
*pindex += nread; /* save new index */
|
||
|
||
/* Read input data and save in l_uint32 */
|
||
inword = 0;
|
||
for (i = 0; i < nread; i++) {
|
||
inbyte = inarray[index + i];
|
||
inword += inbyte << (8 * (3 - i));
|
||
}
|
||
|
||
#if 0
|
||
fprintf(stderr, "index = %d, nread = %d\n", index, nread);
|
||
fprintf(stderr, "inword = %x\n", inword);
|
||
fprintf(stderr, "eof = %d\n", eof);
|
||
#endif
|
||
|
||
/* Special case: output 1 byte only */
|
||
if (inword == 0) {
|
||
outbuf[0] = 'z';
|
||
nbout = 1;
|
||
}
|
||
else { /* output nread + 1 bytes */
|
||
for (i = 4; i >= 4 - nread; i--) {
|
||
val = inword / power85[i];
|
||
outbuf[4 - i] = (l_uint8)(val + '!');
|
||
inword -= val * power85[i];
|
||
}
|
||
nbout = nread + 1;
|
||
}
|
||
*pnbout = nbout;
|
||
|
||
return eof;
|
||
}
|
||
|
||
|
||
/*!
|
||
* decodeAscii85()
|
||
*
|
||
* Input: inarray (ascii85 input data)
|
||
* insize (number of bytes in input array)
|
||
* &outsize (<return> number of bytes in output l_uint8 array)
|
||
* Return: outarray (binary)
|
||
*
|
||
* Notes:
|
||
* (1) We assume the data is properly encoded, so we do not check
|
||
* for invalid characters or the final '>' character.
|
||
* (2) We permit whitespace to be added to the encoding in an
|
||
* arbitrary way.
|
||
*/
|
||
l_uint8 *
|
||
decodeAscii85(char *ina,
|
||
l_int32 insize,
|
||
l_int32 *poutsize)
|
||
{
|
||
char inc;
|
||
char *pin;
|
||
l_uint8 val;
|
||
l_uint8 *outa;
|
||
l_int32 maxsize, ocount, bytecount, index;
|
||
l_uint32 oword;
|
||
|
||
PROCNAME("decodeAscii85");
|
||
|
||
if (!ina)
|
||
return (l_uint8 *)ERROR_PTR("ina not defined", procName, NULL);
|
||
|
||
/* Accumulate results in outa */
|
||
maxsize = (l_int32)(80. + (insize * 4. / 5.)); /* plenty big */
|
||
if ((outa = (l_uint8 *)CALLOC(maxsize, sizeof(l_uint8))) == NULL)
|
||
return (l_uint8 *)ERROR_PTR("outa not made", procName, NULL);
|
||
|
||
pin = ina;
|
||
ocount = 0; /* byte index into outa */
|
||
oword = 0;
|
||
for (index = 0, bytecount = 0; index < insize; index++, pin++) {
|
||
inc = *pin;
|
||
|
||
if (inc == ' ' || inc == '\t' || inc == '\n' ||
|
||
inc == '\f' || inc == '\r' || inc == '\v') /* ignore white space */
|
||
continue;
|
||
|
||
val = inc - '!';
|
||
if (val < 85) {
|
||
oword = oword * 85 + val;
|
||
if (bytecount < 4)
|
||
bytecount++;
|
||
else { /* we have all 5 input chars for the oword */
|
||
outa[ocount] = (oword >> 24) & 0xff;
|
||
outa[ocount + 1] = (oword >> 16) & 0xff;
|
||
outa[ocount + 2] = (oword >> 8) & 0xff;
|
||
outa[ocount + 3] = oword & 0xff;
|
||
ocount += 4;
|
||
bytecount = 0;
|
||
oword = 0;
|
||
}
|
||
}
|
||
else if (inc == 'z' && bytecount == 0) {
|
||
outa[ocount] = 0;
|
||
outa[ocount + 1] = 0;
|
||
outa[ocount + 2] = 0;
|
||
outa[ocount + 3] = 0;
|
||
ocount += 4;
|
||
}
|
||
else if (inc == '~') { /* end of data */
|
||
fprintf(stderr, " %d extra bytes output\n", bytecount - 1);
|
||
switch (bytecount) {
|
||
case 0: /* normal eof */
|
||
case 1: /* error */
|
||
break;
|
||
case 2: /* 1 extra byte */
|
||
oword = oword * (85 * 85 * 85) + 0xffffff;
|
||
outa[ocount] = (oword >> 24) & 0xff;
|
||
break;
|
||
case 3: /* 2 extra bytes */
|
||
oword = oword * (85 * 85) + 0xffff;
|
||
outa[ocount] = (oword >> 24) & 0xff;
|
||
outa[ocount + 1] = (oword >> 16) & 0xff;
|
||
break;
|
||
case 4: /* 3 extra bytes */
|
||
oword = oword * 85 + 0xff;
|
||
outa[ocount] = (oword >> 24) & 0xff;
|
||
outa[ocount + 1] = (oword >> 16) & 0xff;
|
||
outa[ocount + 2] = (oword >> 8) & 0xff;
|
||
break;
|
||
}
|
||
if (bytecount > 1)
|
||
ocount += (bytecount - 1);
|
||
break;
|
||
}
|
||
}
|
||
*poutsize = ocount;
|
||
|
||
return outa;
|
||
}
|
||
|
||
/* --------------------------------------------*/
|
||
#endif /* USE_PSIO */
|
||
/* --------------------------------------------*/
|
||
|