Coverage Report

Created: 2025-06-13 07:15

/src/leptonica/src/psio2.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
3
 -
4
 -  Redistribution and use in source and binary forms, with or without
5
 -  modification, are permitted provided that the following conditions
6
 -  are met:
7
 -  1. Redistributions of source code must retain the above copyright
8
 -     notice, this list of conditions and the following disclaimer.
9
 -  2. Redistributions in binary form must reproduce the above
10
 -     copyright notice, this list of conditions and the following
11
 -     disclaimer in the documentation and/or other materials
12
 -     provided with the distribution.
13
 -
14
 -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
 -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
 -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18
 -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
 -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
 -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
 -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
 -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23
 -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *====================================================================*/
26
27
/*!
28
 * \file psio2.c
29
 * <pre>
30
 *
31
 *    |=============================================================|
32
 *    |                         Important note                      |
33
 *    |=============================================================|
34
 *    | Some of these functions require I/O libraries such as       |
35
 *    | libtiff, libjpeg, and libz.  If you do not have these       |
36
 *    | libraries, some calls will fail.                            |
37
 *    |                                                             |
38
 *    | You can manually deactivate all PostScript writing by       |
39
 *    | setting this in environ.h:                                  |
40
 *    | \code                                                       |
41
 *    |     #define  USE_PSIO     0                                 |
42
 *    | \endcode                                                    |
43
 *    | in environ.h.  This will link psio2stub.c                   |
44
 *    |=============================================================|
45
 *
46
 *     These are lower-level functions that implement a PostScript
47
 *     "device driver" for wrapping images in PostScript.  The images
48
 *     can be rendered by a PostScript interpreter for viewing,
49
 *     using evince or gv.  They can also be rasterized for printing,
50
 *     using gs or an embedded interpreter in a PostScript printer.
51
 *     And they can be converted to a pdf using gs (ps2pdf).
52
 *
53
 *     For uncompressed images
54
 *          l_int32              pixWritePSEmbed()
55
 *          l_int32              pixWriteStreamPS()
56
 *          char                *pixWriteStringPS()
57
 *          char                *generateUncompressedPS()
58
 *          static void          getScaledParametersPS()
59
 *          static l_int32       convertByteToHexAscii()
60
 *
61
 *     For jpeg compressed images (use dct compression)
62
 *          l_int32              convertJpegToPSEmbed()
63
 *          l_int32              convertJpegToPS()
64
 *          static l_int32       convertJpegToPSString()
65
 *          static char         *generateJpegPS()
66
 *
67
 *     For g4 fax compressed images (use ccitt g4 compression)
68
 *          l_int32              convertG4ToPSEmbed()
69
 *          l_int32              convertG4ToPS()
70
 *          static l_int32       convertG4ToPSString()
71
 *          static char         *generateG4PS()
72
 *
73
 *     For multipage tiff images
74
 *          l_int32              convertTiffMultipageToPS()
75
 *
76
 *     For flate (gzip) compressed images (e.g., png)
77
 *          l_int32              convertFlateToPSEmbed()
78
 *          l_int32              convertFlateToPS()
79
 *          static l_int32       convertFlateToPSString()
80
 *          static char         *generateFlatePS()
81
 *
82
 *     Write to memory
83
 *          l_int32              pixWriteMemPS()
84
 *
85
 *     Converting resolution
86
 *          l_int32              getResLetterPage()
87
 *          static l_int32       getResA4Page()
88
 *
89
 *     Setting flag for writing bounding box hint
90
 *          void                 l_psWriteBoundingBox()
91
 *
92
 *  See psio1.c for higher-level functions and their usage.
93
 * </pre>
94
 */
95
96
#ifdef HAVE_CONFIG_H
97
#include <config_auto.h>
98
#endif  /* HAVE_CONFIG_H */
99
100
#include <string.h>
101
#include "allheaders.h"
102
103
/* --------------------------------------------*/
104
#if  USE_PSIO   /* defined in environ.h */
105
 /* --------------------------------------------*/
106
107
    /* Set default for writing bounding box hint */
108
static l_int32  var_PS_WRITE_BOUNDING_BOX = 1;
109
110
#define Bufsize 512
111
static const l_int32  DefaultInputRes = 300;  /* typical scan res, ppi */
112
static const l_int32  MinRes          = 5;
113
static const l_int32  MaxRes          = 3000;
114
115
    /* For computing resolution that fills page to desired amount */
116
static const l_int32  LetterWidth  = 612;   /* points */
117
static const l_int32  LetterHeight = 792;   /* points */
118
static const l_int32  A4Width      = 595;   /* points */
119
static const l_int32  A4Height     = 842;   /* points */
120
static const l_float32  DefaultFillFraction = 0.95f;
121
122
#ifndef  NO_CONSOLE_IO
123
#define  DEBUG_JPEG       0
124
#define  DEBUG_G4         0
125
#define  DEBUG_FLATE      0
126
#endif  /* ~NO_CONSOLE_IO */
127
128
/* Note that the bounding box hint at the top of the generated PostScript
129
 * file is required for the "*Embed" functions.  These generate a
130
 * PostScript file for an individual image that can be translated and
131
 * scaled by an application that embeds the image in its output
132
 * (e.g., in the PS output from a TeX file).
133
 * However, bounding box hints should not be embedded in any
134
 * PostScript image that will be composited with other images,
135
 * where more than one image may be placed in an arbitrary location
136
 * on a page.  */
137
138
    /* Static helper functions */
139
static void getScaledParametersPS(BOX *box, l_int32 wpix, l_int32 hpix,
140
                                  l_int32 res, l_float32 scale,
141
                                  l_float32 *pxpt, l_float32 *pypt,
142
                                  l_float32 *pwpt, l_float32 *phpt);
143
static void convertByteToHexAscii(l_uint8 byteval, char *pnib1, char *pnib2);
144
static l_ok convertJpegToPSString(const char *filein, char **poutstr,
145
                                  l_int32 *pnbytes, l_int32 x, l_int32 y,
146
                                  l_int32 res, l_float32 scale,
147
                                  l_int32 pageno, l_int32 endpage);
148
static char *generateJpegPS(const char *filein, L_COMP_DATA *cid,
149
                            l_float32 xpt, l_float32 ypt, l_float32 wpt,
150
                            l_float32 hpt, l_int32 pageno, l_int32 endpage);
151
static l_ok convertG4ToPSString(const char *filein, char **poutstr,
152
                                l_int32 *pnbytes, l_int32 x, l_int32 y,
153
                                l_int32 res, l_float32 scale, l_int32 pageno,
154
                                l_int32 maskflag, l_int32 endpage);
155
static char *generateG4PS(const char *filein, L_COMP_DATA *cid, l_float32 xpt,
156
                          l_float32 ypt, l_float32 wpt, l_float32 hpt,
157
                          l_int32 maskflag, l_int32 pageno, l_int32 endpage);
158
static l_ok convertFlateToPSString(const char *filein, char **poutstr,
159
                                   l_int32 *pnbytes, l_int32 x, l_int32 y,
160
                                   l_int32 res, l_float32 scale,
161
                                   l_int32 pageno, l_int32 endpage);
162
static char *generateFlatePS(const char *filein, L_COMP_DATA *cid,
163
                             l_float32 xpt, l_float32 ypt, l_float32 wpt,
164
                             l_float32 hpt, l_int32 pageno, l_int32 endpage);
165
166
167
/*-------------------------------------------------------------*
168
 *                  For uncompressed images                    *
169
 *-------------------------------------------------------------*/
170
/*!
171
 * \brief   pixWritePSEmbed()
172
 *
173
 * \param[in]    filein    input file, all depths, colormap OK
174
 * \param[in]    fileout   output ps file
175
 * \return  0 if OK, 1 on error
176
 *
177
 * <pre>
178
 * Notes:
179
 *      (1) This is a simple wrapper function that generates an
180
 *          uncompressed PS file, with a bounding box.
181
 *      (2) The bounding box is required when a program such as TeX
182
 *          (through epsf) places and rescales the image.
183
 *      (3) The bounding box is sized for fitting the image to an
184
 *          8.5 x 11.0 inch page.
185
 * </pre>
186
 */
187
l_ok
188
pixWritePSEmbed(const char  *filein,
189
                const char  *fileout)
190
0
{
191
0
l_int32    w, h, ret;
192
0
l_float32  scale;
193
0
FILE      *fp;
194
0
PIX       *pix;
195
196
0
    if (!filein)
197
0
        return ERROR_INT("filein not defined", __func__, 1);
198
0
    if (!fileout)
199
0
        return ERROR_INT("fileout not defined", __func__, 1);
200
201
0
    if ((pix = pixRead(filein)) == NULL)
202
0
        return ERROR_INT("image not read from file", __func__, 1);
203
0
    w = pixGetWidth(pix);
204
0
    h = pixGetHeight(pix);
205
0
    if (w * 11.0 > h * 8.5)
206
0
        scale = 8.5f * 300.f / (l_float32)w;
207
0
    else
208
0
        scale = 11.0f * 300.f / (l_float32)h;
209
210
0
    if ((fp = fopenWriteStream(fileout, "wb")) == NULL)
211
0
        return ERROR_INT_1("file not opened for write", fileout, __func__, 1);
212
0
    ret = pixWriteStreamPS(fp, pix, NULL, 0, scale);
213
0
    fclose(fp);
214
215
0
    pixDestroy(&pix);
216
0
    return ret;
217
0
}
218
219
220
/*!
221
 * \brief   pixWriteStreamPS()
222
 *
223
 * \param[in]    fp      file stream
224
 * \param[in]    pix
225
 * \param[in]    box     [optional]
226
 * \param[in]    res     can use 0 for default of 300 ppi
227
 * \param[in]    scale   to prevent scaling, use either 1.0 or 0.0
228
 * \return  0 if OK; 1 on error
229
 *
230
 * <pre>
231
 * Notes:
232
 *      (1) This writes image in PS format, optionally scaled,
233
 *          adjusted for the printer resolution, and with
234
 *          a bounding box.
235
 *      (2) For details on use of parameters, see pixWriteStringPS().
236
 * </pre>
237
 */
238
l_ok
239
pixWriteStreamPS(FILE      *fp,
240
                 PIX       *pix,
241
                 BOX       *box,
242
                 l_int32    res,
243
                 l_float32  scale)
244
0
{
245
0
char    *outstr;
246
0
l_int32  length;
247
0
PIX     *pixc;
248
249
0
    if (!fp)
250
0
        return (l_int32)ERROR_INT("stream not open", __func__, 1);
251
0
    if (!pix)
252
0
        return (l_int32)ERROR_INT("pix not defined", __func__, 1);
253
254
0
    if ((pixc = pixConvertForPSWrap(pix)) == NULL)
255
0
        return (l_int32)ERROR_INT("pixc not made", __func__, 1);
256
257
0
    if ((outstr = pixWriteStringPS(pixc, box, res, scale)) == NULL) {
258
0
        pixDestroy(&pixc);
259
0
        return (l_int32)ERROR_INT("outstr not made", __func__, 1);
260
0
    }
261
0
    length = strlen(outstr);
262
0
    fwrite(outstr, 1, length, fp);
263
0
    LEPT_FREE(outstr);
264
0
    pixDestroy(&pixc);
265
0
    return 0;
266
0
}
267
268
269
/*!
270
 * \brief   pixWriteStringPS()
271
 *
272
 * \param[in]    pixs   all depths, colormap OK
273
 * \param[in]    box    bounding box; can be NULL
274
 * \param[in]    res    resolution, in printer ppi.  Use 0 for default 300 ppi.
275
 * \param[in]    scale  scale factor.  If no scaling is desired, use
276
 *                      either 1.0 or 0.0.   Scaling just resets the resolution
277
 *                      parameter; the actual scaling is done in the
278
 *                      interpreter at rendering time.  This is important:
279
 *                      it allows you to scale the image up without
280
 *                      increasing the file size.
281
 * \return  ps string if OK, or NULL on error
282
 *
283
 * <pre>
284
 * a) If %box == NULL, image is placed, optionally scaled,
285
 *      in a standard b.b. at the center of the page.
286
 *      This is to be used when another program like
287
 *      TeX through epsf places the image.
288
 * b) If %box != NULL, image is placed without a
289
 *      b.b. at the specified page location and with
290
 *      optional scaling.  This is to be used when
291
 *      you want to specify exactly where and optionally
292
 *      how big you want the image to be.
293
 *      Note that all coordinates are in PS convention,
294
 *      with 0,0 at LL corner of the page:
295
 *          x,y    location of LL corner of image, in mils.
296
 *          w,h    scaled size, in mils.  Use 0 to
297
 *                 scale with "scale" and "res" input.
298
 *
299
 * %scale: If no scaling is desired, use either 1.0 or 0.0.
300
 * Scaling just resets the resolution parameter; the actual
301
 * scaling is done in the interpreter at rendering time.
302
 * This is important: * it allows you to scale the image up
303
 * without increasing the file size.
304
 *
305
 * Notes:
306
 *      (1) OK, this seems a bit complicated, because there are various
307
 *          ways to scale and not to scale.  Here's a summary:
308
 *      (2) If you don't want any scaling at all:
309
 *           * if you are using a box:
310
 *               set w = 0, h = 0, and use scale = 1.0; it will print
311
 *               each pixel unscaled at printer resolution
312
 *           * if you are not using a box:
313
 *               set scale = 1.0; it will print at printer resolution
314
 *      (3) If you want the image to be a certain size in inches:
315
 *           * you must use a box and set the box (w,h) in mils
316
 *      (4) If you want the image to be scaled by a scale factor != 1.0:
317
 *           * if you are using a box:
318
 *               set w = 0, h = 0, and use the desired scale factor;
319
 *               the higher the printer resolution, the smaller the
320
 *               image will actually appear.
321
 *           * if you are not using a box:
322
 *               set the desired scale factor; the higher the printer
323
 *               resolution, the smaller the image will actually appear.
324
 *      (5) Another complication is the proliferation of distance units:
325
 *           * The interface distances are in milli-inches.
326
 *           * Three different units are used internally:
327
 *              ~ pixels  (units of 1/res inch)
328
 *              ~ printer pts (units of 1/72 inch)
329
 *              ~ inches
330
 *           * Here is a quiz on volume units from a reviewer:
331
 *             How many UK milli-cups in a US kilo-teaspoon?
332
 *               (Hint: 1.0 US cup = 0.75 UK cup + 0.2 US gill;
333
 *                      1.0 US gill = 24.0 US teaspoons)
334
 * </pre>
335
 */
336
char *
337
pixWriteStringPS(PIX       *pixs,
338
                 BOX       *box,
339
                 l_int32    res,
340
                 l_float32  scale)
341
0
{
342
0
char       nib1, nib2;
343
0
char      *hexdata, *outstr;
344
0
l_uint8    byteval;
345
0
l_int32    i, j, k, w, h, d;
346
0
l_float32  wpt, hpt, xpt, ypt;
347
0
l_int32    wpl, psbpl, hexbytes, boxflag, bps;
348
0
l_uint32  *line, *data;
349
0
PIX       *pix;
350
351
0
    if (!pixs)
352
0
        return (char *)ERROR_PTR("pixs not defined", __func__, NULL);
353
354
0
    if ((pix = pixConvertForPSWrap(pixs)) == NULL)
355
0
        return (char *)ERROR_PTR("pix not made", __func__, NULL);
356
0
    pixGetDimensions(pix, &w, &h, &d);
357
358
        /* Get the factors by which PS scales and translates, in pts */
359
0
    if (!box)
360
0
        boxflag = 0;  /* no scaling; b.b. at center */
361
0
    else
362
0
        boxflag = 1;  /* no b.b., specify placement and optional scaling */
363
0
    getScaledParametersPS(box, w, h, res, scale, &xpt, &ypt, &wpt, &hpt);
364
365
0
    if (d == 1)
366
0
        bps = 1;  /* bits/sample */
367
0
    else  /* d == 8 || d == 32 */
368
0
        bps = 8;
369
370
        /* Convert image data to hex string.  psbpl is the number of
371
         * bytes in each raster line when it is packed to the byte
372
         * boundary (not the 32 bit word boundary, as with the pix).
373
         * When converted to hex, the hex string has 2 bytes for
374
         * every byte of raster data. */
375
0
    wpl = pixGetWpl(pix);
376
0
    if (d == 1 || d == 8)
377
0
        psbpl = (w * d + 7) / 8;
378
0
    else /* d == 32 */
379
0
        psbpl = 3 * w;
380
0
    data = pixGetData(pix);
381
0
    hexbytes = 2 * psbpl * h;  /* size of ps hex array */
382
0
    if ((hexdata = (char *)LEPT_CALLOC(hexbytes + 1, sizeof(char))) == NULL)
383
0
        return (char *)ERROR_PTR("hexdata not made", __func__, NULL);
384
0
    if (d == 1 || d == 8) {
385
0
        for (i = 0, k = 0; i < h; i++) {
386
0
            line = data + i * wpl;
387
0
            for (j = 0; j < psbpl; j++) {
388
0
                byteval = GET_DATA_BYTE(line, j);
389
0
                convertByteToHexAscii(byteval, &nib1, &nib2);
390
0
                hexdata[k++] = nib1;
391
0
                hexdata[k++] = nib2;
392
0
            }
393
0
        }
394
0
    } else  {  /* d == 32; hexdata bytes packed RGBRGB..., 2 per sample */
395
0
        for (i = 0, k = 0; i < h; i++) {
396
0
            line = data + i * wpl;
397
0
            for (j = 0; j < w; j++) {
398
0
                byteval = GET_DATA_BYTE(line + j, 0);  /* red */
399
0
                convertByteToHexAscii(byteval, &nib1, &nib2);
400
0
                hexdata[k++] = nib1;
401
0
                hexdata[k++] = nib2;
402
0
                byteval = GET_DATA_BYTE(line + j, 1);  /* green */
403
0
                convertByteToHexAscii(byteval, &nib1, &nib2);
404
0
                hexdata[k++] = nib1;
405
0
                hexdata[k++] = nib2;
406
0
                byteval = GET_DATA_BYTE(line + j, 2);  /* blue */
407
0
                convertByteToHexAscii(byteval, &nib1, &nib2);
408
0
                hexdata[k++] = nib1;
409
0
                hexdata[k++] = nib2;
410
0
            }
411
0
        }
412
0
    }
413
0
    hexdata[k] = '\0';
414
415
0
    outstr = generateUncompressedPS(hexdata, w, h, d, psbpl, bps,
416
0
                                    xpt, ypt, wpt, hpt, boxflag);
417
0
    pixDestroy(&pix);
418
0
    if (!outstr)
419
0
        return (char *)ERROR_PTR("outstr not made", __func__, NULL);
420
0
    return outstr;
421
0
}
422
423
424
/*!
425
 * \brief   generateUncompressedPS()
426
 *
427
 * \param[in]    hexdata
428
 * \param[in]    w, h       raster image size in pixels
429
 * \param[in]    d          image depth in bpp; rgb is 32
430
 * \param[in]    psbpl      raster bytes/line, when packed to the byte boundary
431
 * \param[in]    bps        bits/sample: either 1 or 8
432
 * \param[in]    xpt, ypt   location of LL corner of image, in pts, relative
433
 *                          to the PostScript origin (0,0) at the LL corner
434
 *                          of the page
435
 * \param[in]    wpt, hpt   rendered image size in pts
436
 * \param[in]    boxflag    1 to print out bounding box hint; 0 to skip
437
 * \return  PS string, or NULL on error
438
 *
439
 * <pre>
440
 * Notes:
441
 *      (1) Low-level function.
442
 * </pre>
443
 */
444
char *
445
generateUncompressedPS(char      *hexdata,
446
                       l_int32    w,
447
                       l_int32    h,
448
                       l_int32    d,
449
                       l_int32    psbpl,
450
                       l_int32    bps,
451
                       l_float32  xpt,
452
                       l_float32  ypt,
453
                       l_float32  wpt,
454
                       l_float32  hpt,
455
                       l_int32    boxflag)
456
0
{
457
0
char    *outstr;
458
0
char     bigbuf[Bufsize];
459
0
SARRAY  *sa;
460
461
0
    if (!hexdata)
462
0
        return (char *)ERROR_PTR("hexdata not defined", __func__, NULL);
463
464
0
    sa = sarrayCreate(0);
465
0
    sarrayAddString(sa, "%!Adobe-PS", L_COPY);
466
0
    if (boxflag == 0) {
467
0
        snprintf(bigbuf, sizeof(bigbuf),
468
0
                 "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
469
0
                 xpt, ypt, xpt + wpt, ypt + hpt);
470
0
        sarrayAddString(sa, bigbuf, L_COPY);
471
0
    } else {  /* boxflag == 1 */
472
0
        sarrayAddString(sa, "gsave", L_COPY);
473
0
    }
474
475
0
    if (d == 1)
476
0
        sarrayAddString(sa,
477
0
              "{1 exch sub} settransfer    %invert binary", L_COPY);
478
479
0
    snprintf(bigbuf, sizeof(bigbuf),
480
0
            "/bpl %d string def         %%bpl as a string", psbpl);
481
0
    sarrayAddString(sa, bigbuf, L_COPY);
482
0
    snprintf(bigbuf, sizeof(bigbuf),
483
0
           "%7.2f %7.2f translate         %%set image origin in pts", xpt, ypt);
484
0
    sarrayAddString(sa, bigbuf, L_COPY);
485
0
    snprintf(bigbuf, sizeof(bigbuf),
486
0
            "%7.2f %7.2f scale             %%set image size in pts", wpt, hpt);
487
0
    sarrayAddString(sa, bigbuf, L_COPY);
488
0
    snprintf(bigbuf, sizeof(bigbuf),
489
0
            "%d %d %d                 %%image dimensions in pixels", w, h, bps);
490
0
    sarrayAddString(sa, bigbuf, L_COPY);
491
0
    snprintf(bigbuf, sizeof(bigbuf),
492
0
            "[%d %d %d %d %d %d]     %%mapping matrix: [w 0 0 -h 0 h]",
493
0
            w, 0, 0, -h, 0, h);
494
0
    sarrayAddString(sa, bigbuf, L_COPY);
495
496
0
    if (boxflag == 0) {
497
0
        if (d == 1 || d == 8)
498
0
            sarrayAddString(sa,
499
0
                "{currentfile bpl readhexstring pop} image", L_COPY);
500
0
        else  /* d == 32 */
501
0
            sarrayAddString(sa,
502
0
                "{currentfile bpl readhexstring pop} false 3 colorimage",
503
0
                L_COPY);
504
0
    } else {  /* boxflag == 1 */
505
0
        if (d == 1 || d == 8)
506
0
            sarrayAddString(sa,
507
0
                "{currentfile bpl readhexstring pop} bind image", L_COPY);
508
0
        else  /* d == 32 */
509
0
            sarrayAddString(sa,
510
0
                "{currentfile bpl readhexstring pop} bind false 3 colorimage",
511
0
                L_COPY);
512
0
    }
513
514
0
    sarrayAddString(sa, hexdata, L_INSERT);
515
516
0
    if (boxflag == 0)
517
0
        sarrayAddString(sa, "\nshowpage", L_COPY);
518
0
    else  /* boxflag == 1 */
519
0
        sarrayAddString(sa, "\ngrestore", L_COPY);
520
521
0
    outstr = sarrayToString(sa, 1);
522
0
    sarrayDestroy(&sa);
523
0
    if (!outstr) L_ERROR("outstr not made\n", __func__);
524
0
    return outstr;
525
0
}
526
527
528
/*!
529
 * \brief   getScaledParametersPS()
530
 *
531
 * \param[in]    box     [optional] location of image in mils; x,y is LL corner
532
 * \param[in]    wpix    pix width in pixels
533
 * \param[in]    hpix    pix height in pixels
534
 * \param[in]    res     of printer; use 0 for default
535
 * \param[in]    scale   use 1.0 or 0.0 for no scaling
536
 * \param[out]   pxpt    location of llx in pts
537
 * \param[out]   pypt    location of lly in pts
538
 * \param[out]   pwpt    image width in pts
539
 * \param[out]   phpt    image height in pts
540
 * \return  void no arg checking
541
 *
542
 * <pre>
543
 * Notes:
544
 *      (1) The image is always scaled, depending on res and scale.
545
 *      (2) If no box, the image is centered on the page.
546
 *      (3) If there is a box, the image is placed within it.
547
 * </pre>
548
 */
549
static void
550
getScaledParametersPS(BOX        *box,
551
                      l_int32     wpix,
552
                      l_int32     hpix,
553
                      l_int32     res,
554
                      l_float32   scale,
555
                      l_float32  *pxpt,
556
                      l_float32  *pypt,
557
                      l_float32  *pwpt,
558
                      l_float32  *phpt)
559
0
{
560
0
l_int32    bx, by, bw, bh;
561
0
l_float32  winch, hinch, xinch, yinch, fres;
562
563
0
    if (res == 0)
564
0
        res = DefaultInputRes;
565
0
    fres = (l_float32)res;
566
567
        /* Allow the PS interpreter to scale the resolution */
568
0
    if (scale == 0.0)
569
0
        scale = 1.0;
570
0
    if (scale != 1.0) {
571
0
        fres = (l_float32)res / scale;
572
0
        res = (l_int32)fres;
573
0
    }
574
575
        /* Limit valid resolution interval */
576
0
    if (res < MinRes || res > MaxRes) {
577
0
        L_WARNING("res %d out of bounds; using default res; no scaling\n",
578
0
                  __func__, res);
579
0
        res = DefaultInputRes;
580
0
        fres = (l_float32)res;
581
0
    }
582
583
0
    if (!box) {  /* center on page */
584
0
        winch = (l_float32)wpix / fres;
585
0
        hinch = (l_float32)hpix / fres;
586
0
        xinch = (8.5f - winch) / 2.f;
587
0
        yinch = (11.0f - hinch) / 2.f;
588
0
    } else {
589
0
        boxGetGeometry(box, &bx, &by, &bw, &bh);
590
0
        if (bw == 0)
591
0
            winch = (l_float32)wpix / fres;
592
0
        else
593
0
            winch = (l_float32)bw / 1000.f;
594
0
        if (bh == 0)
595
0
            hinch = (l_float32)hpix / fres;
596
0
        else
597
0
            hinch = (l_float32)bh / 1000.f;
598
0
        xinch = (l_float32)bx / 1000.f;
599
0
        yinch = (l_float32)by / 1000.f;
600
0
    }
601
602
0
    if (xinch < 0)
603
0
        L_WARNING("left edge < 0.0 inch\n", __func__);
604
0
    if (xinch + winch > 8.5)
605
0
        L_WARNING("right edge > 8.5 inch\n", __func__);
606
0
    if (yinch < 0.0)
607
0
        L_WARNING("bottom edge < 0.0 inch\n", __func__);
608
0
    if (yinch + hinch > 11.0)
609
0
        L_WARNING("top edge > 11.0 inch\n", __func__);
610
611
0
    *pwpt = 72.f * winch;
612
0
    *phpt = 72.f * hinch;
613
0
    *pxpt = 72.f * xinch;
614
0
    *pypt = 72.f * yinch;
615
0
    return;
616
0
}
617
618
619
/*!
620
 * \brief   convertByteToHexAscii()
621
 *
622
 * \param[in]    byteval        input byte
623
 * \param[out]   pnib1, pnib2   two hex ascii characters
624
 * \return  void
625
 */
626
static void
627
convertByteToHexAscii(l_uint8  byteval,
628
                      char    *pnib1,
629
                      char    *pnib2)
630
0
{
631
0
l_uint8  nib;
632
633
0
    nib = byteval >> 4;
634
0
    if (nib < 10)
635
0
        *pnib1 = '0' + nib;
636
0
    else
637
0
        *pnib1 = 'a' + (nib - 10);
638
0
    nib = byteval & 0xf;
639
0
    if (nib < 10)
640
0
        *pnib2 = '0' + nib;
641
0
    else
642
0
        *pnib2 = 'a' + (nib - 10);
643
0
    return;
644
0
}
645
646
647
/*-------------------------------------------------------------*
648
 *                  For jpeg compressed images                 *
649
 *-------------------------------------------------------------*/
650
/*!
651
 * \brief   convertJpegToPSEmbed()
652
 *
653
 * \param[in]    filein    input jpeg file
654
 * \param[in]    fileout   output ps file
655
 * \return  0 if OK, 1 on error
656
 *
657
 * <pre>
658
 * Notes:
659
 *      (1) This function takes a jpeg file as input and generates a DCT
660
 *          compressed, ascii85 encoded PS file, with a bounding box.
661
 *      (2) The bounding box is required when a program such as TeX
662
 *          (through epsf) places and rescales the image.
663
 *      (3) The bounding box is sized for fitting the image to an
664
 *          8.5 x 11.0 inch page.
665
 * </pre>
666
 */
667
l_ok
668
convertJpegToPSEmbed(const char  *filein,
669
                     const char  *fileout)
670
0
{
671
0
char         *outstr;
672
0
l_int32       w, h, nbytes, ret;
673
0
l_float32     xpt, ypt, wpt, hpt;
674
0
L_COMP_DATA  *cid;
675
676
0
    if (!filein)
677
0
        return ERROR_INT("filein not defined", __func__, 1);
678
0
    if (!fileout)
679
0
        return ERROR_INT("fileout not defined", __func__, 1);
680
681
        /* Generate the ascii encoded jpeg data */
682
0
    if ((cid = l_generateJpegData(filein, 1)) == NULL)
683
0
        return ERROR_INT("jpeg data not made", __func__, 1);
684
0
    w = cid->w;
685
0
    h = cid->h;
686
687
        /* Scale for 20 pt boundary and otherwise full filling
688
         * in one direction on 8.5 x 11 inch device */
689
0
    xpt = 20.0;
690
0
    ypt = 20.0;
691
0
    if (w * 11.0 > h * 8.5) {
692
0
        wpt = 572.0;   /* 612 - 2 * 20 */
693
0
        hpt = wpt * (l_float32)h / (l_float32)w;
694
0
    } else {
695
0
        hpt = 752.0;   /* 792 - 2 * 20 */
696
0
        wpt = hpt * (l_float32)w / (l_float32)h;
697
0
    }
698
699
        /* Generate the PS.
700
         * The bounding box information should be inserted (default). */
701
0
    outstr = generateJpegPS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1);
702
0
    l_CIDataDestroy(&cid);
703
0
    if (!outstr)
704
0
        return ERROR_INT("outstr not made", __func__, 1);
705
0
    nbytes = strlen(outstr);
706
707
0
    ret = l_binaryWrite(fileout, "w", outstr, nbytes);
708
0
    LEPT_FREE(outstr);
709
0
    if (ret) L_ERROR("ps string not written to file\n", __func__);
710
0
    return ret;
711
0
}
712
713
714
/*!
715
 * \brief   convertJpegToPS()
716
 *
717
 * \param[in]    filein     input jpeg file
718
 * \param[in]    fileout    output ps file
719
 * \param[in]    operation  "w" for write; "a" for append
720
 * \param[in]    x, y       location of LL corner of image, in pixels, relative
721
 *                          to the PostScript origin (0,0) at the LL corner
722
 *                          of the page
723
 * \param[in]    res        resolution of the input image, in ppi;
724
 *                          use 0 for default
725
 * \param[in]    scale      scaling by printer; use 0.0 or 1.0 for no scaling
726
 * \param[in]    pageno     page number; must start with 1; you can use 0
727
 *                          if there is only one page
728
 * \param[in]    endpage    boolean: use TRUE if this is the last image to be
729
 *                          added to the page; FALSE otherwise
730
 * \return  0 if OK, 1 on error
731
 *
732
 * <pre>
733
 * Notes:
734
 *      (1) This is simpler to use than pixWriteStringPS(), and
735
 *          it outputs in level 2 PS as compressed DCT (overlaid
736
 *          with ascii85 encoding).
737
 *      (2) An output file can contain multiple pages, each with
738
 *          multiple images.  The arguments to convertJpegToPS()
739
 *          allow you to control placement of jpeg images on multiple
740
 *          pages within a PostScript file.
741
 *      (3) For the first image written to a file, use "w", which
742
 *          opens for write and clears the file.  For all subsequent
743
 *          images written to that file, use "a".
744
 *      (4) The (x, y) parameters give the LL corner of the image
745
 *          relative to the LL corner of the page.  They are in
746
 *          units of pixels if scale = 1.0.  If you use (e.g.)
747
 *          scale = 2.0, the image is placed at (2x, 2y) on the page,
748
 *          and the image dimensions are also doubled.
749
 *      (5) Display vs printed resolution:
750
 *           * If your display is 75 ppi and your image was created
751
 *             at a resolution of 300 ppi, you can get the image
752
 *             to print at the same size as it appears on your display
753
 *             by either setting scale = 4.0 or by setting  res = 75.
754
 *             Both tell the printer to make a 4x enlarged image.
755
 *           * If your image is generated at 150 ppi and you use scale = 1,
756
 *             it will be rendered such that 150 pixels correspond
757
 *             to 72 pts (1 inch on the printer).  This function does
758
 *             the conversion from pixels (with or without scaling) to
759
 *             pts, which are the units that the printer uses.
760
 *           * The printer will choose its own resolution to use
761
 *             in rendering the image, which will not affect the size
762
 *             of the rendered image.  That is because the output
763
 *             PostScript file describes the geometry in terms of pts,
764
 *             which are defined to be 1/72 inch.  The printer will
765
 *             only see the size of the image in pts, through the
766
 *             scale and translate parameters and the affine
767
 *             transform (the ImageMatrix) of the image.
768
 *      (6) To render multiple images on the same page, set
769
 *          endpage = FALSE for each image until you get to the
770
 *          last, for which you set endpage = TRUE.  This causes the
771
 *          "showpage" command to be invoked.  Showpage outputs
772
 *          the entire page and clears the raster buffer for the
773
 *          next page to be added.  Without a "showpage",
774
 *          subsequent images from the next page will overlay those
775
 *          previously put down.
776
 *      (7) For multiple pages, increment the page number, starting
777
 *          with page 1.  This allows PostScript (and PDF) to build
778
 *          a page directory, which viewers use for navigation.
779
 * </pre>
780
 */
781
l_ok
782
convertJpegToPS(const char  *filein,
783
                const char  *fileout,
784
                const char  *operation,
785
                l_int32      x,
786
                l_int32      y,
787
                l_int32      res,
788
                l_float32    scale,
789
                l_int32      pageno,
790
                l_int32      endpage)
791
0
{
792
0
char    *outstr;
793
0
l_int32  nbytes;
794
795
0
    if (!filein)
796
0
        return ERROR_INT("filein not defined", __func__, 1);
797
0
    if (!fileout)
798
0
        return ERROR_INT("fileout not defined", __func__, 1);
799
0
    if (strcmp(operation, "w") && strcmp(operation, "a"))
800
0
        return ERROR_INT("operation must be \"w\" or \"a\"", __func__, 1);
801
802
0
    if (convertJpegToPSString(filein, &outstr, &nbytes, x, y, res, scale,
803
0
                          pageno, endpage))
804
0
        return ERROR_INT("ps string not made", __func__, 1);
805
806
0
    if (l_binaryWrite(fileout, operation, outstr, nbytes)) {
807
0
        LEPT_FREE(outstr);
808
0
        return ERROR_INT("ps string not written to file", __func__, 1);
809
0
    }
810
811
0
    LEPT_FREE(outstr);
812
0
    return 0;
813
0
}
814
815
816
/*!
817
 * \brief   convertJpegToPSString()
818
 *
819
 *      Generates PS string in jpeg format from jpeg file
820
 *
821
 * \param[in]    filein     input jpeg file
822
 * \param[out]   poutstr    PS string
823
 * \param[out]   pnbytes    number of bytes in PS string
824
 * \param[in]    x, y       location of LL corner of image, in pixels, relative
825
 *                          to the PostScript origin (0,0) at the LL corner
826
 *                           of the page
827
 * \param[in]    res        resolution of the input image, in ppi;
828
 *                          use 0 for default
829
 * \param[in]    scale      scaling by printer; use 0.0 or 1.0 for no scaling
830
 * \param[in]    pageno     page number; must start with 1; you can use 0
831
 *                          if there is only one page
832
 * \param[in]    endpage    boolean: use TRUE if this is the last image to be
833
 *                          added to the page; FALSE otherwise
834
 * \return  0 if OK, 1 on error
835
 *
836
 * <pre>
837
 * Notes:
838
 *      (1) For usage, see convertJpegToPS()
839
 * </pre>
840
 */
841
static l_ok
842
convertJpegToPSString(const char  *filein,
843
                      char       **poutstr,
844
                      l_int32     *pnbytes,
845
                      l_int32      x,
846
                      l_int32      y,
847
                      l_int32      res,
848
                      l_float32    scale,
849
                      l_int32      pageno,
850
                      l_int32      endpage)
851
0
{
852
0
char         *outstr;
853
0
l_float32     xpt, ypt, wpt, hpt;
854
0
L_COMP_DATA  *cid;
855
856
0
    if (!poutstr)
857
0
        return ERROR_INT("&outstr not defined", __func__, 1);
858
0
    if (!pnbytes)
859
0
        return ERROR_INT("&nbytes not defined", __func__, 1);
860
0
    *poutstr = NULL;
861
0
    *pnbytes = 0;
862
0
    if (!filein)
863
0
        return ERROR_INT("filein not defined", __func__, 1);
864
865
        /* Generate the ascii encoded jpeg data */
866
0
    if ((cid = l_generateJpegData(filein, 1)) == NULL)
867
0
        return ERROR_INT("jpeg data not made", __func__, 1);
868
869
        /* Get scaled location in pts.  Guess the input scan resolution
870
         * based on the input parameter %res, the resolution data in
871
         * the pix, and the size of the image. */
872
0
    if (scale == 0.0)
873
0
        scale = 1.0;
874
0
    if (res <= 0) {
875
0
        if (cid->res > 0)
876
0
            res = cid->res;
877
0
        else
878
0
            res = DefaultInputRes;
879
0
    }
880
881
        /* Get scaled location in pts */
882
0
    if (scale == 0.0)
883
0
        scale = 1.0;
884
0
    xpt = scale * x * 72.f / res;
885
0
    ypt = scale * y * 72.f / res;
886
0
    wpt = scale * cid->w * 72.f / res;
887
0
    hpt = scale * cid->h * 72.f / res;
888
889
0
    if (pageno == 0)
890
0
        pageno = 1;
891
892
#if  DEBUG_JPEG
893
    lept_stderr("w = %d, h = %d, bps = %d, spp = %d\n",
894
                cid->w, cid->h, cid->bps, cid->spp);
895
    lept_stderr("comp bytes = %ld, nbytes85 = %ld, ratio = %5.3f\n",
896
                (unsigned long)cid->nbytescomp, (unsigned long)cid->nbytes85,
897
                (l_float32)cid->nbytes85 / (l_float32)cid->nbytescomp);
898
    lept_stderr("xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n",
899
                xpt, ypt, wpt, hpt);
900
#endif   /* DEBUG_JPEG */
901
902
        /* Generate the PS */
903
0
    outstr = generateJpegPS(NULL, cid, xpt, ypt, wpt, hpt, pageno, endpage);
904
0
    l_CIDataDestroy(&cid);
905
0
    if (!outstr)
906
0
        return ERROR_INT("outstr not made", __func__, 1);
907
0
    *poutstr = outstr;
908
0
    *pnbytes = strlen(outstr);
909
0
    return 0;
910
0
}
911
912
913
/*!
914
 * \brief   generateJpegPS()
915
 *
916
 * \param[in]    filein     [optional] input jpeg filename; can be null
917
 * \param[in]    cid        jpeg compressed image data
918
 * \param[in]    xpt, ypt   location of LL corner of image, in pts, relative
919
 *                          to the PostScript origin (0,0) at the LL corner
920
 *                          of the page
921
 * \param[in]    wpt, hpt   rendered image size in pts
922
 * \param[in]    pageno     page number; must start with 1; you can use 0
923
 *                          if there is only one page.
924
 * \param[in]    endpage    boolean: use TRUE if this is the last image to be
925
 *                          added to the page; FALSE otherwise
926
 * \return  PS string, or NULL on error
927
 *
928
 * <pre>
929
 * Notes:
930
 *      (1) Low-level function.
931
 * </pre>
932
 */
933
static char *
934
generateJpegPS(const char   *filein,
935
               L_COMP_DATA  *cid,
936
               l_float32     xpt,
937
               l_float32     ypt,
938
               l_float32     wpt,
939
               l_float32     hpt,
940
               l_int32       pageno,
941
               l_int32       endpage)
942
0
{
943
0
l_int32  w, h, bps, spp;
944
0
char    *outstr;
945
0
char     bigbuf[Bufsize];
946
0
SARRAY  *sa;
947
948
0
    if (!cid)
949
0
        return (char *)ERROR_PTR("jpeg data not defined", __func__, NULL);
950
0
    w = cid->w;
951
0
    h = cid->h;
952
0
    bps = cid->bps;
953
0
    spp = cid->spp;
954
955
0
    sa = sarrayCreate(50);
956
0
    sarrayAddString(sa, "%!PS-Adobe-3.0", L_COPY);
957
0
    sarrayAddString(sa, "%%Creator: leptonica", L_COPY);
958
0
    if (filein)
959
0
        snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein);
960
0
    else
961
0
        snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: Jpeg compressed PS");
962
0
    sarrayAddString(sa, bigbuf, L_COPY);
963
0
    sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY);
964
965
0
    if (var_PS_WRITE_BOUNDING_BOX == 1) {
966
0
        snprintf(bigbuf, sizeof(bigbuf),
967
0
                 "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
968
0
                 xpt, ypt, xpt + wpt, ypt + hpt);
969
0
        sarrayAddString(sa, bigbuf, L_COPY);
970
0
    }
971
972
0
    sarrayAddString(sa, "%%LanguageLevel: 2", L_COPY);
973
0
    sarrayAddString(sa, "%%EndComments", L_COPY);
974
0
    snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno);
975
0
    sarrayAddString(sa, bigbuf, L_COPY);
976
977
0
    sarrayAddString(sa, "save", L_COPY);
978
0
    sarrayAddString(sa,
979
0
                    "/RawData currentfile /ASCII85Decode filter def", L_COPY);
980
0
    sarrayAddString(sa, "/Data RawData << >> /DCTDecode filter def", L_COPY);
981
982
0
    snprintf(bigbuf, sizeof(bigbuf),
983
0
        "%7.2f %7.2f translate         %%set image origin in pts", xpt, ypt);
984
0
    sarrayAddString(sa, bigbuf, L_COPY);
985
986
0
    snprintf(bigbuf, sizeof(bigbuf),
987
0
        "%7.2f %7.2f scale             %%set image size in pts", wpt, hpt);
988
0
    sarrayAddString(sa, bigbuf, L_COPY);
989
990
0
    if (spp == 1)
991
0
        sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY);
992
0
    else if (spp == 3)
993
0
        sarrayAddString(sa, "/DeviceRGB setcolorspace", L_COPY);
994
0
    else  /*spp == 4 */
995
0
        sarrayAddString(sa, "/DeviceCMYK setcolorspace", L_COPY);
996
997
0
    sarrayAddString(sa, "{ << /ImageType 1", L_COPY);
998
0
    snprintf(bigbuf, sizeof(bigbuf), "     /Width %d", w);
999
0
    sarrayAddString(sa, bigbuf, L_COPY);
1000
0
    snprintf(bigbuf, sizeof(bigbuf), "     /Height %d", h);
1001
0
    sarrayAddString(sa, bigbuf, L_COPY);
1002
0
    snprintf(bigbuf, sizeof(bigbuf),
1003
0
            "     /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h);
1004
0
    sarrayAddString(sa, bigbuf, L_COPY);
1005
0
    sarrayAddString(sa, "     /DataSource Data", L_COPY);
1006
0
    snprintf(bigbuf, sizeof(bigbuf), "     /BitsPerComponent %d", bps);
1007
0
    sarrayAddString(sa, bigbuf, L_COPY);
1008
1009
0
    if (spp == 1)
1010
0
        sarrayAddString(sa, "     /Decode [0 1]", L_COPY);
1011
0
    else if (spp == 3)
1012
0
        sarrayAddString(sa, "     /Decode [0 1 0 1 0 1]", L_COPY);
1013
0
    else   /* spp == 4 */
1014
0
        sarrayAddString(sa, "     /Decode [0 1 0 1 0 1 0 1]", L_COPY);
1015
1016
0
    sarrayAddString(sa, "  >> image", L_COPY);
1017
0
    sarrayAddString(sa, "  Data closefile", L_COPY);
1018
0
    sarrayAddString(sa, "  RawData flushfile", L_COPY);
1019
0
    if (endpage == TRUE)
1020
0
        sarrayAddString(sa, "  showpage", L_COPY);
1021
0
    sarrayAddString(sa, "  restore", L_COPY);
1022
0
    sarrayAddString(sa, "} exec", L_COPY);
1023
1024
        /* Insert the ascii85 jpeg data; this is now owned by sa */
1025
0
    sarrayAddString(sa, cid->data85, L_INSERT);
1026
0
    cid->data85 = NULL;  /* it has been transferred and destroyed */
1027
1028
        /* Generate and return the output string */
1029
0
    outstr = sarrayToString(sa, 1);
1030
0
    sarrayDestroy(&sa);
1031
0
    return outstr;
1032
0
}
1033
1034
1035
/*-------------------------------------------------------------*
1036
 *                  For ccitt g4 compressed images             *
1037
 *-------------------------------------------------------------*/
1038
/*!
1039
 * \brief   convertG4ToPSEmbed()
1040
 *
1041
 * \param[in]    filein    input tiff file
1042
 * \param[in]    fileout   output ps file
1043
 * \return  0 if OK, 1 on error
1044
 *
1045
 * <pre>
1046
 * Notes:
1047
 *      (1) This function takes a g4 compressed tif file as input and
1048
 *          generates a g4 compressed, ascii85 encoded PS file, with
1049
 *          a bounding box.
1050
 *      (2) The bounding box is required when a program such as TeX
1051
 *          (through epsf) places and rescales the image.
1052
 *      (3) The bounding box is sized for fitting the image to an
1053
 *          8.5 x 11.0 inch page.
1054
 *      (4) We paint this through a mask, over whatever is below.
1055
 * </pre>
1056
 */
1057
l_ok
1058
convertG4ToPSEmbed(const char  *filein,
1059
                   const char  *fileout)
1060
0
{
1061
0
char         *outstr;
1062
0
l_int32       w, h, nbytes, ret;
1063
0
l_float32     xpt, ypt, wpt, hpt;
1064
0
L_COMP_DATA  *cid;
1065
1066
0
    if (!filein)
1067
0
        return ERROR_INT("filein not defined", __func__, 1);
1068
0
    if (!fileout)
1069
0
        return ERROR_INT("fileout not defined", __func__, 1);
1070
1071
0
    if ((cid = l_generateG4Data(filein, 1)) == NULL)
1072
0
        return ERROR_INT("g4 data not made", __func__, 1);
1073
0
    w = cid->w;
1074
0
    h = cid->h;
1075
1076
        /* Scale for 20 pt boundary and otherwise full filling
1077
         * in one direction on 8.5 x 11 inch device */
1078
0
    xpt = 20.0;
1079
0
    ypt = 20.0;
1080
0
    if (w * 11.0 > h * 8.5) {
1081
0
        wpt = 572.0;   /* 612 - 2 * 20 */
1082
0
        hpt = wpt * (l_float32)h / (l_float32)w;
1083
0
    } else {
1084
0
        hpt = 752.0;   /* 792 - 2 * 20 */
1085
0
        wpt = hpt * (l_float32)w / (l_float32)h;
1086
0
    }
1087
1088
        /* Generate the PS, painting through the image mask.
1089
         * The bounding box information should be inserted (default). */
1090
0
    outstr = generateG4PS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1, 1);
1091
0
    l_CIDataDestroy(&cid);
1092
0
    if (!outstr)
1093
0
        return ERROR_INT("outstr not made", __func__, 1);
1094
0
    nbytes = strlen(outstr);
1095
1096
0
    ret = l_binaryWrite(fileout, "w", outstr, nbytes);
1097
0
    LEPT_FREE(outstr);
1098
0
    if (ret) L_ERROR("ps string not written to file\n", __func__);
1099
0
    return ret;
1100
0
}
1101
1102
1103
/*!
1104
 * \brief   convertG4ToPS()
1105
 *
1106
 * \param[in]    filein     input tiff g4 file
1107
 * \param[in]    fileout    output ps file
1108
 * \param[in]    operation  "w" for write; "a" for append
1109
 * \param[in]    x, y       location of LL corner of image, in pixels, relative
1110
 *                          to the PostScript origin (0,0) at the LL corner
1111
 *                          of the page
1112
 * \param[in]    res        resolution of the input image, in ppi; typ. values
1113
 *                          are 300 and 600; use 0 for automatic determination
1114
 *                          based on image size
1115
 * \param[in]    scale      scaling by printer; use 0.0 or 1.0 for no scaling
1116
 * \param[in]    pageno     page number; must start with 1; you can use 0
1117
 *                          if there is only one page.
1118
 * \param[in]    maskflag   boolean: use TRUE if just painting through fg;
1119
 *                          FALSE if painting both fg and bg.
1120
 * \param[in]    endpage    boolean: use TRUE if this is the last image to be
1121
 *                          added to the page; FALSE otherwise
1122
 * \return  0 if OK, 1 on error
1123
 *
1124
 * <pre>
1125
 * Notes:
1126
 *      (1) See the usage comments in convertJpegToPS(), some of
1127
 *          which are repeated here.
1128
 *      (2) This is a wrapper for tiff g4.  The PostScript that
1129
 *          is generated is expanded by about 5/4 (due to the
1130
 *          ascii85 encoding.  If you convert to pdf (ps2pdf), the
1131
 *          ascii85 decoder is automatically invoked, so that the
1132
 *          pdf wrapped g4 file is essentially the same size as
1133
 *          the original g4 file.  It's useful to have the PS
1134
 *          file ascii85 encoded, because many printers will not
1135
 *          print binary PS files.
1136
 *      (3) For the first image written to a file, use "w", which
1137
 *          opens for write and clears the file.  For all subsequent
1138
 *          images written to that file, use "a".
1139
 *      (4) To render multiple images on the same page, set
1140
 *          endpage = FALSE for each image until you get to the
1141
 *          last, for which you set endpage = TRUE.  This causes the
1142
 *          "showpage" command to be invoked.  Showpage outputs
1143
 *          the entire page and clears the raster buffer for the
1144
 *          next page to be added.  Without a "showpage",
1145
 *          subsequent images from the next page will overlay those
1146
 *          previously put down.
1147
 *      (5) For multiple images to the same page, where you are writing
1148
 *          both jpeg and tiff-g4, you have two options:
1149
 *           (a) write the g4 first, as either image (maskflag == FALSE)
1150
 *               or imagemask (maskflag == TRUE), and then write the
1151
 *               jpeg over it.
1152
 *           (b) write the jpeg first and as the last item, write
1153
 *               the g4 as an imagemask (maskflag == TRUE), to paint
1154
 *               through the foreground only.
1155
 *          We have this flexibility with the tiff-g4 because it is 1 bpp.
1156
 *      (6) For multiple pages, increment the page number, starting
1157
 *          with page 1.  This allows PostScript (and PDF) to build
1158
 *          a page directory, which viewers use for navigation.
1159
 * </pre>
1160
 */
1161
l_ok
1162
convertG4ToPS(const char  *filein,
1163
              const char  *fileout,
1164
              const char  *operation,
1165
              l_int32      x,
1166
              l_int32      y,
1167
              l_int32      res,
1168
              l_float32    scale,
1169
              l_int32      pageno,
1170
              l_int32      maskflag,
1171
              l_int32      endpage)
1172
0
{
1173
0
char    *outstr;
1174
0
l_int32  nbytes, ret;
1175
1176
0
    if (!filein)
1177
0
        return ERROR_INT("filein not defined", __func__, 1);
1178
0
    if (!fileout)
1179
0
        return ERROR_INT("fileout not defined", __func__, 1);
1180
0
    if (strcmp(operation, "w") && strcmp(operation, "a"))
1181
0
        return ERROR_INT("operation must be \"w\" or \"a\"", __func__, 1);
1182
1183
0
    if (convertG4ToPSString(filein, &outstr, &nbytes, x, y, res, scale,
1184
0
                            pageno, maskflag, endpage))
1185
0
        return ERROR_INT("ps string not made", __func__, 1);
1186
1187
0
    ret = l_binaryWrite(fileout, operation, outstr, nbytes);
1188
0
    LEPT_FREE(outstr);
1189
0
    if (ret)
1190
0
        return ERROR_INT("ps string not written to file", __func__, 1);
1191
0
    return 0;
1192
0
}
1193
1194
1195
/*!
1196
 * \brief   convertG4ToPSString()
1197
 *
1198
 * \param[in]    filein     input tiff g4 file
1199
 * \param[out]   poutstr    PS string
1200
 * \param[out]   pnbytes    number of bytes in PS string
1201
 * \param[in]    x, y       location of LL corner of image, in pixels, relative
1202
 *                          to the PostScript origin (0,0) at the LL corner
1203
 *                          of the page
1204
 * \param[in]    res        resolution of the input image, in ppi; typ. values
1205
 *                          are 300 and 600; use 0 for automatic determination
1206
 *                          based on image size
1207
 * \param[in]    scale      scaling by printer; use 0.0 or 1.0 for no scaling
1208
 * \param[in]    pageno     page number; must start with 1; you can use 0
1209
 *                          if there is only one page.
1210
 * \param[in]    maskflag   boolean: use TRUE if just painting through fg;
1211
 *                          FALSE if painting both fg and bg.
1212
 * \param[in]    endpage    boolean: use TRUE if this is the last image to be
1213
 *                          added to the page; FALSE otherwise
1214
 * \return  0 if OK, 1 on error
1215
 *
1216
 * <pre>
1217
 * Notes:
1218
 *      (1) Generates PS string in G4 compressed tiff format from G4 tiff file.
1219
 *      (2) For usage, see convertG4ToPS().
1220
 * </pre>
1221
 */
1222
static l_ok
1223
convertG4ToPSString(const char  *filein,
1224
                    char       **poutstr,
1225
                    l_int32     *pnbytes,
1226
                    l_int32      x,
1227
                    l_int32      y,
1228
                    l_int32      res,
1229
                    l_float32    scale,
1230
                    l_int32      pageno,
1231
                    l_int32      maskflag,
1232
                    l_int32      endpage)
1233
0
{
1234
0
char         *outstr;
1235
0
l_float32     xpt, ypt, wpt, hpt;
1236
0
L_COMP_DATA  *cid;
1237
1238
0
    if (!poutstr)
1239
0
        return ERROR_INT("&outstr not defined", __func__, 1);
1240
0
    if (!pnbytes)
1241
0
        return ERROR_INT("&nbytes not defined", __func__, 1);
1242
0
    *poutstr = NULL;
1243
0
    *pnbytes = 0;
1244
0
    if (!filein)
1245
0
        return ERROR_INT("filein not defined", __func__, 1);
1246
1247
0
    if ((cid = l_generateG4Data(filein, 1)) == NULL)
1248
0
        return ERROR_INT("g4 data not made", __func__, 1);
1249
1250
        /* Get scaled location in pts.  Guess the input scan resolution
1251
         * based on the input parameter %res, the resolution data in
1252
         * the pix, and the size of the image. */
1253
0
    if (scale == 0.0)
1254
0
        scale = 1.0;
1255
0
    if (res <= 0) {
1256
0
        if (cid->res > 0) {
1257
0
            res = cid->res;
1258
0
        } else {
1259
0
            if (cid->h <= 3509)  /* A4 height at 300 ppi */
1260
0
                res = 300;
1261
0
            else
1262
0
                res = 600;
1263
0
        }
1264
0
    }
1265
0
    xpt = scale * x * 72.f / res;
1266
0
    ypt = scale * y * 72.f / res;
1267
0
    wpt = scale * cid->w * 72.f / res;
1268
0
    hpt = scale * cid->h * 72.f / res;
1269
1270
0
    if (pageno == 0)
1271
0
        pageno = 1;
1272
1273
#if  DEBUG_G4
1274
    lept_stderr("w = %d, h = %d, minisblack = %d\n",
1275
                cid->w, cid->h, cid->minisblack);
1276
    lept_stderr("comp bytes = %ld, nbytes85 = %ld\n",
1277
                (unsigned long)cid->nbytescomp, (unsigned long)cid->nbytes85);
1278
    lept_stderr("xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n",
1279
                xpt, ypt, wpt, hpt);
1280
#endif   /* DEBUG_G4 */
1281
1282
        /* Generate the PS */
1283
0
    outstr = generateG4PS(NULL, cid, xpt, ypt, wpt, hpt,
1284
0
                          maskflag, pageno, endpage);
1285
0
    l_CIDataDestroy(&cid);
1286
0
    if (!outstr)
1287
0
        return ERROR_INT("outstr not made", __func__, 1);
1288
0
    *poutstr = outstr;
1289
0
    *pnbytes = strlen(outstr);
1290
0
    return 0;
1291
0
}
1292
1293
1294
/*!
1295
 * \brief   generateG4PS()
1296
 *
1297
 * \param[in]    filein     [optional] input tiff g4 file; can be null
1298
 * \param[in]    cid g4     compressed image data
1299
 * \param[in]    xpt, ypt   location of LL corner of image, in pts, relative
1300
 *                          to the PostScript origin (0,0) at the LL corner
1301
 *                          of the page
1302
 * \param[in]    wpt, hpt   rendered image size in pts
1303
 * \param[in]    maskflag   boolean: use TRUE if just painting through fg;
1304
 *                          FALSE if painting both fg and bg.
1305
 * \param[in]    pageno     page number; must start with 1; you can use 0
1306
 *                          if there is only one page.
1307
 * \param[in]    endpage    boolean: use TRUE if this is the last image to be
1308
 *                          added to the page; FALSE otherwise
1309
 * \return  PS string, or NULL on error
1310
 *
1311
 * <pre>
1312
 * Notes:
1313
 *      (1) Low-level function.
1314
 * </pre>
1315
 */
1316
static char *
1317
generateG4PS(const char   *filein,
1318
             L_COMP_DATA  *cid,
1319
             l_float32     xpt,
1320
             l_float32     ypt,
1321
             l_float32     wpt,
1322
             l_float32     hpt,
1323
             l_int32       maskflag,
1324
             l_int32       pageno,
1325
             l_int32       endpage)
1326
0
{
1327
0
l_int32  w, h;
1328
0
char    *outstr;
1329
0
char     bigbuf[Bufsize];
1330
0
SARRAY  *sa;
1331
1332
0
    if (!cid)
1333
0
        return (char *)ERROR_PTR("g4 data not defined", __func__, NULL);
1334
0
    w = cid->w;
1335
0
    h = cid->h;
1336
1337
0
    sa = sarrayCreate(50);
1338
0
    sarrayAddString(sa, "%!PS-Adobe-3.0", L_COPY);
1339
0
    sarrayAddString(sa, "%%Creator: leptonica", L_COPY);
1340
0
    if (filein)
1341
0
        snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein);
1342
0
    else
1343
0
        snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: G4 compressed PS");
1344
0
    sarrayAddString(sa, bigbuf, L_COPY);
1345
0
    sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY);
1346
1347
0
    if (var_PS_WRITE_BOUNDING_BOX == 1) {
1348
0
        snprintf(bigbuf, sizeof(bigbuf),
1349
0
            "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
1350
0
                    xpt, ypt, xpt + wpt, ypt + hpt);
1351
0
        sarrayAddString(sa, bigbuf, L_COPY);
1352
0
    }
1353
1354
0
    sarrayAddString(sa, "%%LanguageLevel: 2", L_COPY);
1355
0
    sarrayAddString(sa, "%%EndComments", L_COPY);
1356
0
    snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno);
1357
0
    sarrayAddString(sa, bigbuf, L_COPY);
1358
1359
0
    sarrayAddString(sa, "save", L_COPY);
1360
0
    sarrayAddString(sa, "100 dict begin", L_COPY);
1361
1362
0
    snprintf(bigbuf, sizeof(bigbuf),
1363
0
        "%7.2f %7.2f translate         %%set image origin in pts", xpt, ypt);
1364
0
    sarrayAddString(sa, bigbuf, L_COPY);
1365
1366
0
    snprintf(bigbuf, sizeof(bigbuf),
1367
0
        "%7.2f %7.2f scale             %%set image size in pts", wpt, hpt);
1368
0
    sarrayAddString(sa, bigbuf, L_COPY);
1369
1370
0
    sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY);
1371
1372
0
    sarrayAddString(sa, "{", L_COPY);
1373
0
    sarrayAddString(sa,
1374
0
          "  /RawData currentfile /ASCII85Decode filter def", L_COPY);
1375
0
    sarrayAddString(sa, "  << ", L_COPY);
1376
0
    sarrayAddString(sa, "    /ImageType 1", L_COPY);
1377
0
    snprintf(bigbuf, sizeof(bigbuf), "    /Width %d", w);
1378
0
    sarrayAddString(sa, bigbuf, L_COPY);
1379
0
    snprintf(bigbuf, sizeof(bigbuf), "    /Height %d", h);
1380
0
    sarrayAddString(sa, bigbuf, L_COPY);
1381
0
    snprintf(bigbuf, sizeof(bigbuf),
1382
0
             "    /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h);
1383
0
    sarrayAddString(sa, bigbuf, L_COPY);
1384
0
    sarrayAddString(sa, "    /BitsPerComponent 1", L_COPY);
1385
0
    sarrayAddString(sa, "    /Interpolate true", L_COPY);
1386
0
    if (cid->minisblack)
1387
0
        sarrayAddString(sa, "    /Decode [1 0]", L_COPY);
1388
0
    else  /* miniswhite; typical for 1 bpp */
1389
0
        sarrayAddString(sa, "    /Decode [0 1]", L_COPY);
1390
0
    sarrayAddString(sa, "    /DataSource RawData", L_COPY);
1391
0
    sarrayAddString(sa, "        <<", L_COPY);
1392
0
    sarrayAddString(sa, "          /K -1", L_COPY);
1393
0
    snprintf(bigbuf, sizeof(bigbuf), "          /Columns %d", w);
1394
0
    sarrayAddString(sa, bigbuf, L_COPY);
1395
0
    snprintf(bigbuf, sizeof(bigbuf), "          /Rows %d", h);
1396
0
    sarrayAddString(sa, bigbuf, L_COPY);
1397
0
    sarrayAddString(sa, "        >> /CCITTFaxDecode filter", L_COPY);
1398
0
    if (maskflag == TRUE)  /* just paint through the fg */
1399
0
        sarrayAddString(sa, "  >> imagemask", L_COPY);
1400
0
    else  /* Paint full image */
1401
0
        sarrayAddString(sa, "  >> image", L_COPY);
1402
0
    sarrayAddString(sa, "  RawData flushfile", L_COPY);
1403
0
    if (endpage == TRUE)
1404
0
        sarrayAddString(sa, "  showpage", L_COPY);
1405
0
    sarrayAddString(sa, "}", L_COPY);
1406
1407
0
    sarrayAddString(sa, "%%BeginData:", L_COPY);
1408
0
    sarrayAddString(sa, "exec", L_COPY);
1409
1410
        /* Insert the ascii85 ccittg4 data; this is now owned by sa */
1411
0
    sarrayAddString(sa, cid->data85, L_INSERT);
1412
1413
        /* Concat the trailing data */
1414
0
    sarrayAddString(sa, "%%EndData", L_COPY);
1415
0
    sarrayAddString(sa, "end", L_COPY);
1416
0
    sarrayAddString(sa, "restore", L_COPY);
1417
1418
0
    outstr = sarrayToString(sa, 1);
1419
0
    sarrayDestroy(&sa);
1420
0
    cid->data85 = NULL;  /* it has been transferred and destroyed */
1421
0
    return outstr;
1422
0
}
1423
1424
1425
/*-------------------------------------------------------------*
1426
 *                     For tiff multipage files                *
1427
 *-------------------------------------------------------------*/
1428
/*!
1429
 * \brief   convertTiffMultipageToPS()
1430
 *
1431
 * \param[in]    filein      input tiff multipage file
1432
 * \param[in]    fileout     output ps file
1433
 * \param[in]    fillfract   factor for filling 8.5 x 11 inch page;
1434
 *                           use 0.0 for DefaultFillFraction
1435
 * \return  0 if OK, 1 on error
1436
 *
1437
 * <pre>
1438
 * Notes:
1439
 *      (1) This converts a multipage tiff file of binary page images
1440
 *          into a ccitt g4 compressed PS file.
1441
 *      (2) If the images are generated from a standard resolution fax,
1442
 *          the vertical resolution is doubled to give a normal-looking
1443
 *          aspect ratio.
1444
 * </pre>
1445
 */
1446
l_ok
1447
convertTiffMultipageToPS(const char  *filein,
1448
                         const char  *fileout,
1449
                         l_float32    fillfract)
1450
0
{
1451
0
char      *tempfile;
1452
0
l_int32    i, npages, w, h, istiff;
1453
0
l_float32  scale;
1454
0
PIX       *pix, *pixs;
1455
0
FILE      *fp;
1456
1457
0
    if (!filein)
1458
0
        return ERROR_INT("filein not defined", __func__, 1);
1459
0
    if (!fileout)
1460
0
        return ERROR_INT("fileout not defined", __func__, 1);
1461
1462
0
    if ((fp = fopenReadStream(filein)) == NULL)
1463
0
        return ERROR_INT_1("file not found", filein, __func__, 1);
1464
0
    istiff = fileFormatIsTiff(fp);
1465
0
    if (!istiff) {
1466
0
        fclose(fp);
1467
0
        return ERROR_INT_1("file not tiff format", filein, __func__, 1);
1468
0
    }
1469
0
    tiffGetCount(fp, &npages);
1470
0
    fclose(fp);
1471
1472
0
    if (fillfract == 0.0)
1473
0
        fillfract = DefaultFillFraction;
1474
1475
0
    for (i = 0; i < npages; i++) {
1476
0
        if ((pix = pixReadTiff(filein, i)) == NULL)
1477
0
            return ERROR_INT_1("pix not made", filein, __func__, 1);
1478
1479
0
        pixGetDimensions(pix, &w, &h, NULL);
1480
0
        if (w == 1728 && h < w)   /* it's a std res fax */
1481
0
            pixs = pixScale(pix, 1.0, 2.0);
1482
0
        else
1483
0
            pixs = pixClone(pix);
1484
1485
0
        tempfile = l_makeTempFilename();
1486
0
        pixWrite(tempfile, pixs, IFF_TIFF_G4);
1487
0
        scale = L_MIN(fillfract * 2550 / w, fillfract * 3300 / h);
1488
0
        if (i == 0)
1489
0
            convertG4ToPS(tempfile, fileout, "w", 0, 0, 300, scale,
1490
0
                          i + 1, FALSE, TRUE);
1491
0
        else
1492
0
            convertG4ToPS(tempfile, fileout, "a", 0, 0, 300, scale,
1493
0
                          i + 1, FALSE, TRUE);
1494
0
        lept_rmfile(tempfile);
1495
0
        LEPT_FREE(tempfile);
1496
0
        pixDestroy(&pix);
1497
0
        pixDestroy(&pixs);
1498
0
    }
1499
1500
0
    return 0;
1501
0
}
1502
1503
1504
/*---------------------------------------------------------------------*
1505
 *            For flate (gzip) compressed images (e.g., png)           *
1506
 *---------------------------------------------------------------------*/
1507
/*!
1508
 * \brief   convertFlateToPSEmbed()
1509
 *
1510
 * \param[in]    filein    input file -- any format
1511
 * \param[in]    fileout   output ps file
1512
 * \return  0 if OK, 1 on error
1513
 *
1514
 * <pre>
1515
 * Notes:
1516
 *      (1) This function takes any image file as input and generates a
1517
 *          flate-compressed, ascii85 encoded PS file, with a bounding box.
1518
 *      (2) The bounding box is required when a program such as TeX
1519
 *          (through epsf) places and rescales the image.
1520
 *      (3) The bounding box is sized for fitting the image to an
1521
 *          8.5 x 11.0 inch page.
1522
 * </pre>
1523
 */
1524
l_ok
1525
convertFlateToPSEmbed(const char  *filein,
1526
                      const char  *fileout)
1527
0
{
1528
0
char         *outstr;
1529
0
l_int32       w, h, nbytes, ret;
1530
0
l_float32     xpt, ypt, wpt, hpt;
1531
0
L_COMP_DATA  *cid;
1532
1533
0
    if (!filein)
1534
0
        return ERROR_INT("filein not defined", __func__, 1);
1535
0
    if (!fileout)
1536
0
        return ERROR_INT("fileout not defined", __func__, 1);
1537
1538
0
    if ((cid = l_generateFlateData(filein, 1)) == NULL)
1539
0
        return ERROR_INT("flate data not made", __func__, 1);
1540
0
    w = cid->w;
1541
0
    h = cid->h;
1542
1543
        /* Scale for 20 pt boundary and otherwise full filling
1544
         * in one direction on 8.5 x 11 inch device */
1545
0
    xpt = 20.0;
1546
0
    ypt = 20.0;
1547
0
    if (w * 11.0 > h * 8.5) {
1548
0
        wpt = 572.0;   /* 612 - 2 * 20 */
1549
0
        hpt = wpt * (l_float32)h / (l_float32)w;
1550
0
    } else {
1551
0
        hpt = 752.0;   /* 792 - 2 * 20 */
1552
0
        wpt = hpt * (l_float32)w / (l_float32)h;
1553
0
    }
1554
1555
        /* Generate the PS.
1556
         * The bounding box information should be inserted (default). */
1557
0
    outstr = generateFlatePS(NULL, cid, xpt, ypt, wpt, hpt, 1, 1);
1558
0
    l_CIDataDestroy(&cid);
1559
0
    if (!outstr)
1560
0
        return ERROR_INT("outstr not made", __func__, 1);
1561
0
    nbytes = strlen(outstr);
1562
1563
0
    ret = l_binaryWrite(fileout, "w", outstr, nbytes);
1564
0
    LEPT_FREE(outstr);
1565
0
    if (ret) L_ERROR("ps string not written to file\n", __func__);
1566
0
    return ret;
1567
0
}
1568
1569
1570
/*!
1571
 * \brief   convertFlateToPS()
1572
 *
1573
 * \param[in]    filein    input file -- any format
1574
 * \param[in]    fileout    output ps file
1575
 * \param[in]    operation  "w" for write; "a" for append
1576
 * \param[in]    x, y       location of LL corner of image, in pixels, relative
1577
 *                          to the PostScript origin (0,0) at the LL corner
1578
 *                          of the page
1579
 * \param[in]    res        resolution of the input image, in ppi;
1580
 *                          use 0 for default
1581
 * \param[in]    scale      scaling by printer; use 0.0 or 1.0 for no scaling
1582
 * \param[in]    pageno     page number; must start with 1; you can use 0
1583
 *                          if there is only one page.
1584
 * \param[in]    endpage    boolean: use TRUE if this is the last image to be
1585
 *                          added to the page; FALSE otherwise
1586
 * \return  0 if OK, 1 on error
1587
 *
1588
 * <pre>
1589
 * Notes:
1590
 *      (1) This outputs level 3 PS as flate compressed (overlaid
1591
 *          with ascii85 encoding).
1592
 *      (2) An output file can contain multiple pages, each with
1593
 *          multiple images.  The arguments to convertFlateToPS()
1594
 *          allow you to control placement of png images on multiple
1595
 *          pages within a PostScript file.
1596
 *      (3) For the first image written to a file, use "w", which
1597
 *          opens for write and clears the file.  For all subsequent
1598
 *          images written to that file, use "a".
1599
 *      (4) The (x, y) parameters give the LL corner of the image
1600
 *          relative to the LL corner of the page.  They are in
1601
 *          units of pixels if scale = 1.0.  If you use (e.g.)
1602
 *          scale = 2.0, the image is placed at (2x, 2y) on the page,
1603
 *          and the image dimensions are also doubled.
1604
 *      (5) Display vs printed resolution:
1605
 *           * If your display is 75 ppi and your image was created
1606
 *             at a resolution of 300 ppi, you can get the image
1607
 *             to print at the same size as it appears on your display
1608
 *             by either setting scale = 4.0 or by setting  res = 75.
1609
 *             Both tell the printer to make a 4x enlarged image.
1610
 *           * If your image is generated at 150 ppi and you use scale = 1,
1611
 *             it will be rendered such that 150 pixels correspond
1612
 *             to 72 pts (1 inch on the printer).  This function does
1613
 *             the conversion from pixels (with or without scaling) to
1614
 *             pts, which are the units that the printer uses.
1615
 *           * The printer will choose its own resolution to use
1616
 *             in rendering the image, which will not affect the size
1617
 *             of the rendered image.  That is because the output
1618
 *             PostScript file describes the geometry in terms of pts,
1619
 *             which are defined to be 1/72 inch.  The printer will
1620
 *             only see the size of the image in pts, through the
1621
 *             scale and translate parameters and the affine
1622
 *             transform (the ImageMatrix) of the image.
1623
 *      (6) To render multiple images on the same page, set
1624
 *          endpage = FALSE for each image until you get to the
1625
 *          last, for which you set endpage = TRUE.  This causes the
1626
 *          "showpage" command to be invoked.  Showpage outputs
1627
 *          the entire page and clears the raster buffer for the
1628
 *          next page to be added.  Without a "showpage",
1629
 *          subsequent images from the next page will overlay those
1630
 *          previously put down.
1631
 *      (7) For multiple pages, increment the page number, starting
1632
 *          with page 1.  This allows PostScript (and PDF) to build
1633
 *          a page directory, which viewers use for navigation.
1634
 * </pre>
1635
 */
1636
l_ok
1637
convertFlateToPS(const char  *filein,
1638
                 const char  *fileout,
1639
                 const char  *operation,
1640
                 l_int32      x,
1641
                 l_int32      y,
1642
                 l_int32      res,
1643
                 l_float32    scale,
1644
                 l_int32      pageno,
1645
                 l_int32      endpage)
1646
0
{
1647
0
char    *outstr;
1648
0
l_int32  nbytes, ret;
1649
1650
0
    if (!filein)
1651
0
        return ERROR_INT("filein not defined", __func__, 1);
1652
0
    if (!fileout)
1653
0
        return ERROR_INT("fileout not defined", __func__, 1);
1654
0
    if (strcmp(operation, "w") && strcmp(operation, "a"))
1655
0
        return ERROR_INT("operation must be \"w\" or \"a\"", __func__, 1);
1656
1657
0
    if (convertFlateToPSString(filein, &outstr, &nbytes, x, y, res, scale,
1658
0
                               pageno, endpage))
1659
0
        return ERROR_INT("ps string not made", __func__, 1);
1660
1661
0
    ret = l_binaryWrite(fileout, operation, outstr, nbytes);
1662
0
    LEPT_FREE(outstr);
1663
0
    if (ret) L_ERROR("ps string not written to file\n", __func__);
1664
0
    return ret;
1665
0
}
1666
1667
1668
/*!
1669
 * \brief   convertFlateToPSString()
1670
 *
1671
 *      Generates level 3 PS string in flate compressed format.
1672
 *
1673
 * \param[in]    filein    input image file
1674
 * \param[out]   poutstr   PS string
1675
 * \param[out]   pnbytes   number of bytes in PS string
1676
 * \param[in]    x, y      location of LL corner of image, in pixels, relative
1677
 *                         to the PostScript origin (0,0) at the LL corner
1678
 *                         of the page
1679
 * \param[in]    res       resolution of the input image, in ppi;
1680
 *                         use 0 for default
1681
 * \param[in]    scale     scaling by printer; use 0.0 or 1.0 for no scaling
1682
 * \param[in]    pageno    page number; must start with 1; you can use 0
1683
 *                         if there is only one page.
1684
 * \param[in]    endpage   boolean: use TRUE if this is the last image to be
1685
 *                         added to the page; FALSE otherwise
1686
 * \return  0 if OK, 1 on error
1687
 *
1688
 * <pre>
1689
 * Notes:
1690
 *      (1) The returned PS character array is a null-terminated
1691
 *          ascii string.  All the raster data is ascii85 encoded, so
1692
 *          there are no null bytes embedded in it.
1693
 *      (2) The raster encoding is made with gzip, the same as that
1694
 *          in a png file that is compressed without prediction.
1695
 *          The raster data itself is 25% larger than that in the
1696
 *          binary form, due to the ascii85 encoding.
1697
 *
1698
 *  Usage:  See convertFlateToPS()
1699
 * </pre>
1700
 */
1701
static l_ok
1702
convertFlateToPSString(const char  *filein,
1703
                       char       **poutstr,
1704
                       l_int32     *pnbytes,
1705
                       l_int32      x,
1706
                       l_int32      y,
1707
                       l_int32      res,
1708
                       l_float32    scale,
1709
                       l_int32      pageno,
1710
                       l_int32      endpage)
1711
0
{
1712
0
char         *outstr;
1713
0
l_float32     xpt, ypt, wpt, hpt;
1714
0
L_COMP_DATA  *cid;
1715
1716
0
    if (!poutstr)
1717
0
        return ERROR_INT("&outstr not defined", __func__, 1);
1718
0
    if (!pnbytes)
1719
0
        return ERROR_INT("&nbytes not defined", __func__, 1);
1720
0
    *pnbytes = 0;
1721
0
    *poutstr = NULL;
1722
0
    if (!filein)
1723
0
        return ERROR_INT("filein not defined", __func__, 1);
1724
1725
0
    if ((cid = l_generateFlateData(filein, 1)) == NULL)
1726
0
        return ERROR_INT("flate data not made", __func__, 1);
1727
1728
        /* Get scaled location in pts.  Guess the input scan resolution
1729
         * based on the input parameter %res, the resolution data in
1730
         * the pix, and the size of the image. */
1731
0
    if (scale == 0.0)
1732
0
        scale = 1.0;
1733
0
    if (res <= 0) {
1734
0
        if (cid->res > 0)
1735
0
            res = cid->res;
1736
0
        else
1737
0
            res = DefaultInputRes;
1738
0
    }
1739
0
    xpt = scale * x * 72.f / res;
1740
0
    ypt = scale * y * 72.f / res;
1741
0
    wpt = scale * cid->w * 72.f / res;
1742
0
    hpt = scale * cid->h * 72.f / res;
1743
1744
0
    if (pageno == 0)
1745
0
        pageno = 1;
1746
1747
#if  DEBUG_FLATE
1748
    lept_stderr("w = %d, h = %d, bps = %d, spp = %d\n",
1749
                cid->w, cid->h, cid->bps, cid->spp);
1750
    lept_stderr("uncomp bytes = %ld, comp bytes = %ld, nbytes85 = %ld\n",
1751
                (unsigned long)cid->nbytes, (unsigned long)cid->nbytescomp,
1752
                (unsigned long)cid->nbytes85);
1753
    lept_stderr("xpt = %7.2f, ypt = %7.2f, wpt = %7.2f, hpt = %7.2f\n",
1754
                xpt, ypt, wpt, hpt);
1755
#endif   /* DEBUG_FLATE */
1756
1757
        /* Generate the PS */
1758
0
    outstr = generateFlatePS(NULL, cid, xpt, ypt, wpt, hpt, pageno, endpage);
1759
0
    l_CIDataDestroy(&cid);
1760
0
    if (!outstr)
1761
0
        return ERROR_INT("outstr not made", __func__, 1);
1762
0
    *poutstr = outstr;
1763
0
    *pnbytes = strlen(outstr);
1764
0
    return 0;
1765
0
}
1766
1767
1768
/*!
1769
 * \brief   generateFlatePS()
1770
 *
1771
 * \param[in]    filein      [optional] input filename; can be null
1772
 * \param[in]    cid         flate compressed image data
1773
 * \param[in]    xpt, ypt    location of LL corner of image, in pts, relative
1774
 *                           to the PostScript origin (0,0) at the LL corner
1775
 *                           of the page
1776
 * \param[in]    wpt, hpt    rendered image size in pts
1777
 * \param[in]    pageno      page number; must start with 1; you can use 0
1778
 *                           if there is only one page
1779
 * \param[in]    endpage     boolean: use TRUE if this is the last image to be
1780
 *                           added to the page; FALSE otherwise
1781
 * \return  PS string, or NULL on error
1782
 */
1783
static char *
1784
generateFlatePS(const char   *filein,
1785
                L_COMP_DATA  *cid,
1786
                l_float32     xpt,
1787
                l_float32     ypt,
1788
                l_float32     wpt,
1789
                l_float32     hpt,
1790
                l_int32       pageno,
1791
                l_int32       endpage)
1792
0
{
1793
0
l_int32  w, h, bps, spp;
1794
0
char    *outstr;
1795
0
char     bigbuf[Bufsize];
1796
0
SARRAY  *sa;
1797
1798
0
    if (!cid)
1799
0
        return (char *)ERROR_PTR("flate data not defined", __func__, NULL);
1800
0
    w = cid->w;
1801
0
    h = cid->h;
1802
0
    bps = cid->bps;
1803
0
    spp = cid->spp;
1804
1805
0
    sa = sarrayCreate(50);
1806
0
    sarrayAddString(sa, "%!PS-Adobe-3.0 EPSF-3.0", L_COPY);
1807
0
    sarrayAddString(sa, "%%Creator: leptonica", L_COPY);
1808
0
    if (filein)
1809
0
        snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: %s", filein);
1810
0
    else
1811
0
        snprintf(bigbuf, sizeof(bigbuf), "%%%%Title: Flate compressed PS");
1812
0
    sarrayAddString(sa, bigbuf, L_COPY);
1813
0
    sarrayAddString(sa, "%%DocumentData: Clean7Bit", L_COPY);
1814
1815
0
    if (var_PS_WRITE_BOUNDING_BOX == 1) {
1816
0
        snprintf(bigbuf, sizeof(bigbuf),
1817
0
                 "%%%%BoundingBox: %7.2f %7.2f %7.2f %7.2f",
1818
0
                 xpt, ypt, xpt + wpt, ypt + hpt);
1819
0
        sarrayAddString(sa, bigbuf, L_COPY);
1820
0
    }
1821
1822
0
    sarrayAddString(sa, "%%LanguageLevel: 3", L_COPY);
1823
0
    sarrayAddString(sa, "%%EndComments", L_COPY);
1824
0
    snprintf(bigbuf, sizeof(bigbuf), "%%%%Page: %d %d", pageno, pageno);
1825
0
    sarrayAddString(sa, bigbuf, L_COPY);
1826
1827
0
    sarrayAddString(sa, "save", L_COPY);
1828
0
    snprintf(bigbuf, sizeof(bigbuf),
1829
0
           "%7.2f %7.2f translate         %%set image origin in pts", xpt, ypt);
1830
0
    sarrayAddString(sa, bigbuf, L_COPY);
1831
1832
0
    snprintf(bigbuf, sizeof(bigbuf),
1833
0
             "%7.2f %7.2f scale             %%set image size in pts", wpt, hpt);
1834
0
    sarrayAddString(sa, bigbuf, L_COPY);
1835
1836
        /* If there is a colormap, add the data; it is now owned by sa */
1837
0
    if (cid->cmapdata85) {
1838
0
        snprintf(bigbuf, sizeof(bigbuf),
1839
0
                 "[ /Indexed /DeviceRGB %d          %%set colormap type/size",
1840
0
                 cid->ncolors - 1);
1841
0
        sarrayAddString(sa, bigbuf, L_COPY);
1842
0
        sarrayAddString(sa, "  <~", L_COPY);
1843
0
        sarrayAddString(sa, cid->cmapdata85, L_INSERT);
1844
0
        sarrayAddString(sa, "  ] setcolorspace", L_COPY);
1845
0
    } else if (spp == 1) {
1846
0
        sarrayAddString(sa, "/DeviceGray setcolorspace", L_COPY);
1847
0
    } else {  /* spp == 3 */
1848
0
        sarrayAddString(sa, "/DeviceRGB setcolorspace", L_COPY);
1849
0
    }
1850
1851
0
    sarrayAddString(sa,
1852
0
                    "/RawData currentfile /ASCII85Decode filter def", L_COPY);
1853
0
    sarrayAddString(sa,
1854
0
                    "/Data RawData << >> /FlateDecode filter def", L_COPY);
1855
1856
0
    sarrayAddString(sa, "{ << /ImageType 1", L_COPY);
1857
0
    snprintf(bigbuf, sizeof(bigbuf), "     /Width %d", w);
1858
0
    sarrayAddString(sa, bigbuf, L_COPY);
1859
0
    snprintf(bigbuf, sizeof(bigbuf), "     /Height %d", h);
1860
0
    sarrayAddString(sa, bigbuf, L_COPY);
1861
0
    snprintf(bigbuf, sizeof(bigbuf), "     /BitsPerComponent %d", bps);
1862
0
    sarrayAddString(sa, bigbuf, L_COPY);
1863
0
    snprintf(bigbuf, sizeof(bigbuf),
1864
0
            "     /ImageMatrix [ %d 0 0 %d 0 %d ]", w, -h, h);
1865
0
    sarrayAddString(sa, bigbuf, L_COPY);
1866
1867
0
    if (cid->cmapdata85) {
1868
0
        sarrayAddString(sa, "     /Decode [0 255]", L_COPY);
1869
0
    } else if (spp == 1) {
1870
0
        if (bps == 1)  /* miniswhite photometry */
1871
0
            sarrayAddString(sa, "     /Decode [1 0]", L_COPY);
1872
0
        else  /* bps > 1 */
1873
0
            sarrayAddString(sa, "     /Decode [0 1]", L_COPY);
1874
0
    } else {  /* spp == 3 */
1875
0
        sarrayAddString(sa, "     /Decode [0 1 0 1 0 1]", L_COPY);
1876
0
    }
1877
1878
0
    sarrayAddString(sa, "     /DataSource Data", L_COPY);
1879
0
    sarrayAddString(sa, "  >> image", L_COPY);
1880
0
    sarrayAddString(sa, "  Data closefile", L_COPY);
1881
0
    sarrayAddString(sa, "  RawData flushfile", L_COPY);
1882
0
    if (endpage == TRUE)
1883
0
        sarrayAddString(sa, "  showpage", L_COPY);
1884
0
    sarrayAddString(sa, "  restore", L_COPY);
1885
0
    sarrayAddString(sa, "} exec", L_COPY);
1886
1887
        /* Insert the ascii85 gzipped data; this is now owned by sa */
1888
0
    sarrayAddString(sa, cid->data85, L_INSERT);
1889
1890
        /* Generate and return the output string */
1891
0
    outstr = sarrayToString(sa, 1);
1892
0
    sarrayDestroy(&sa);
1893
0
    cid->cmapdata85 = NULL;  /* it has been transferred to sa and destroyed */
1894
0
    cid->data85 = NULL;  /* it has been transferred to sa and destroyed */
1895
0
    return outstr;
1896
0
}
1897
1898
1899
/*---------------------------------------------------------------------*
1900
 *                          Write to memory                            *
1901
 *---------------------------------------------------------------------*/
1902
/*!
1903
 * \brief   pixWriteMemPS()
1904
 *
1905
 * \param[out]   pdata    data of tiff compressed image
1906
 * \param[out]   psize    size of returned data
1907
 * \param[in]    pix
1908
 * \param[in]    box      [optional]
1909
 * \param[in]    res      can use 0 for default of 300 ppi
1910
 * \param[in]    scale    to prevent scaling, use either 1.0 or 0.0
1911
 * \return  0 if OK, 1 on error
1912
 *
1913
 * <pre>
1914
 * Notes:
1915
 *      (1) See pixWriteStringPS() for usage.
1916
 *      (2) This is just a wrapper for pixWriteStringPS(), which
1917
 *          writes uncompressed image data to memory.
1918
 * </pre>
1919
 */
1920
l_ok
1921
pixWriteMemPS(l_uint8  **pdata,
1922
              size_t    *psize,
1923
              PIX       *pix,
1924
              BOX       *box,
1925
              l_int32    res,
1926
              l_float32  scale)
1927
0
{
1928
0
    if (!pdata)
1929
0
        return ERROR_INT("&data not defined", __func__, 1 );
1930
0
    if (!psize)
1931
0
        return ERROR_INT("&size not defined", __func__, 1 );
1932
0
    if (!pix)
1933
0
        return ERROR_INT("&pix not defined", __func__, 1 );
1934
1935
0
    *pdata = (l_uint8 *)pixWriteStringPS(pix, box, res, scale);
1936
0
    *psize = strlen((char *)(*pdata));
1937
0
    return 0;
1938
0
}
1939
1940
1941
/*-------------------------------------------------------------*
1942
 *                    Converting resolution                    *
1943
 *-------------------------------------------------------------*/
1944
/*!
1945
 * \brief   getResLetterPage()
1946
 *
1947
 * \param[in]    w           image width, pixels
1948
 * \param[in]    h           image height, pixels
1949
 * \param[in]    fillfract   fraction in linear dimension of full page,
1950
 *                           not to be exceeded; use 0 for default
1951
 * \return  resolution
1952
 */
1953
l_int32
1954
getResLetterPage(l_int32    w,
1955
                 l_int32    h,
1956
                 l_float32  fillfract)
1957
0
{
1958
0
l_int32  resw, resh, res;
1959
1960
0
    if (fillfract == 0.0)
1961
0
        fillfract = DefaultFillFraction;
1962
0
    resw = (l_int32)((w * 72.) / (LetterWidth * fillfract));
1963
0
    resh = (l_int32)((h * 72.) / (LetterHeight * fillfract));
1964
0
    res = L_MAX(resw, resh);
1965
0
    return res;
1966
0
}
1967
1968
1969
/*!
1970
 * \brief   getResA4Page()
1971
 *
1972
 * \param[in]    w           image width, pixels
1973
 * \param[in]    h           image height, pixels
1974
 * \param[in]    fillfract   fraction in linear dimension of full page,
1975
 *                           not to be exceeded; use 0 for default
1976
 * \return  resolution
1977
 */
1978
l_int32
1979
getResA4Page(l_int32    w,
1980
             l_int32    h,
1981
             l_float32  fillfract)
1982
0
{
1983
0
l_int32  resw, resh, res;
1984
1985
0
    if (fillfract == 0.0)
1986
0
        fillfract = DefaultFillFraction;
1987
0
    resw = (l_int32)((w * 72.) / (A4Width * fillfract));
1988
0
    resh = (l_int32)((h * 72.) / (A4Height * fillfract));
1989
0
    res = L_MAX(resw, resh);
1990
0
    return res;
1991
0
}
1992
1993
1994
/*-------------------------------------------------------------*
1995
 *           Setting flag for writing bounding box hint        *
1996
 *-------------------------------------------------------------*/
1997
void
1998
l_psWriteBoundingBox(l_int32  flag)
1999
0
{
2000
0
    var_PS_WRITE_BOUNDING_BOX = flag;
2001
0
}
2002
2003
2004
/* --------------------------------------------*/
2005
#endif  /* USE_PSIO */
2006
/* --------------------------------------------*/