libtiff/tools/fax2ps.c
Frank Warmerdam 4f4c99abfc From Helge (libtiff@oldach.net)
Here's a fix for fax2ps that corrects behaviour for non-Letter paper
sizes. It fixes two problems:

Without scaling (-S) the fax is now centered on the page size specified
with -H and/or -W. Before, fax2ps was using an obscure and practially
useless algorithm to allocate the image relative to Letter sized paper
which sometime sled to useless whitespace on the paper, while at the
same time cutting of the faxes printable area at the opposite border.

Second, scaling now preserves aspect ratio, which makes unusual faxes
(in particular short ones) print properly.
2001-03-29 01:45:43 +00:00

430 lines
12 KiB
C

/* $Header: /cvs/maptools/cvsroot/libtiff/tools/fax2ps.c,v 1.5 2001-03-29 01:45:43 warmerda Exp $" */
/*
* Copyright (c) 1991-1997 Sam Leffler
* Copyright (c) 1991-1997 Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that (i) the above copyright notices and this permission notice appear in
* all copies of the software and related documentation, and (ii) the names of
* Sam Leffler and Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Sam Leffler and Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
* ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#if defined(VMS)
#include <unixio.h>
#elif defined(_WINDOWS)
#include <io.h>
#define off_t toff_t
#else
#include <unistd.h>
#endif
#include "tiffio.h"
float defxres = 204.; /* default x resolution (pixels/inch) */
float defyres = 98.; /* default y resolution (lines/inch) */
const float basePageWidth = 8.5;
const float basePageHeight = 11.0;
const float half = 0.5;
const float points = 72.0;
float pageWidth = 8.5; /* image page width (inches) */
float pageHeight = 11.0; /* image page length (inches) */
int scaleToPage = 0; /* if true, scale raster to page dimensions */
int totalPages = 0; /* total # pages printed */
int row; /* current output row */
int maxline = 512; /* max output line of PostScript */
/*
* Turn a bit-mapped scanline into the appropriate sequence
* of PostScript characters to be rendered.
*
* Original version written by Bret D. Whissel,
* Florida State University Meteorology Department
* March 13-15, 1995.
*/
static void
printruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx)
{
static struct {
char white, black;
short width;
} WBarr[] = {
{ 'd', 'n', 512 }, { 'e', 'o', 256 }, { 'f', 'p', 128 },
{ 'g', 'q', 64 }, { 'h', 'r', 32 }, { 'i', 's', 16 },
{ 'j', 't', 8 }, { 'k', 'u', 4 }, { 'l', 'v', 2 },
{ 'm', 'w', 1 }
};
static char* svalue =
" !\"#$&'*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abc";
int colormode = 1; /* 0 for white, 1 for black */
int runlength = 0;
int n = maxline;
int x = 0;
int l;
(void) buf;
printf("%d m(", row++);
while (runs < erun) {
if (!runlength) {
colormode ^= 1;
runlength = *runs++;
if (x+runlength > lastx)
runlength = runs[-1] = lastx-x;
x += runlength;
if (!colormode && runs == erun)
break; /* don't bother printing the final white run */
}
/*
* If a runlength is greater than 6 pixels, then spit out
* black or white characters until the runlength drops to
* 6 or less. Once a runlength is <= 6, then combine black
* and white runlengths until a 6-pixel pattern is obtained.
* Then write out the special character. Six-pixel patterns
* were selected since 64 patterns is the largest power of
* two less than the 92 "easily printable" PostScript
* characters (i.e., no escape codes or octal chars).
*/
l = 0;
while (runlength > 6) { /* Run is greater than six... */
if (runlength >= WBarr[l].width) {
if (n == 0) {
putchar('\n');
n = maxline;
}
putchar(colormode ? WBarr[l].black : WBarr[l].white), n--;
runlength -= WBarr[l].width;
} else
l++;
}
while (runlength > 0 && runlength <= 6) {
int bitsleft = 6;
int t = 0;
while (bitsleft) {
if (runlength <= bitsleft) {
if (colormode)
t |= ((1 << runlength)-1) << (bitsleft-runlength);
bitsleft -= runlength;
runlength = 0;
if (bitsleft) {
if (runs >= erun)
break;
colormode ^= 1;
runlength = *runs++;
if (x+runlength > lastx)
runlength = runs[-1] = lastx-x;
x += runlength;
}
} else { /* runlength exceeds bits left */
if (colormode)
t |= ((1 << bitsleft)-1);
runlength -= bitsleft;
bitsleft = 0;
}
}
if (n == 0) {
putchar('\n');
n = maxline;
}
putchar(svalue[t]), n--;
}
}
printf(")s\n");
}
void
printTIF(TIFF* tif, int pageNumber)
{
uint32 w, h;
uint16 unit;
float xres, yres, scale = 1.0;
tstrip_t s, ns;
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
if (!TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres)) {
TIFFWarning(TIFFFileName(tif),
"No x-resolution, assuming %g dpi", defxres);
xres = defxres;
}
if (!TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres)) {
TIFFWarning(TIFFFileName(tif),
"No y-resolution, assuming %g lpi", defyres);
yres = defyres; /* XXX */
}
if (TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &unit) &&
unit == RESUNIT_CENTIMETER) {
xres *= 25.4;
yres *= 25.4;
}
printf("%%%%Page: \"%d\" %d\n", pageNumber, pageNumber);
printf("/$pageTop save def gsave\n");
if (scaleToPage)
scale = pageHeight / (h/yres) < pageWidth / (w/xres) ?
pageHeight / (h/yres) : pageWidth / (w/xres);
printf("%g %g translate\n",
points * (pageWidth - scale*w/xres) * half,
points * (scale*h/yres + (pageHeight - scale*h/yres) * half));
printf("%g %g scale\n", points/xres*scale, -points/yres*scale);
printf("0 setgray\n");
TIFFSetField(tif, TIFFTAG_FAXFILLFUNC, printruns);
ns = TIFFNumberOfStrips(tif);
row = 0;
for (s = 0; s < ns; s++)
(void) TIFFReadEncodedStrip(tif, s, (tdata_t) NULL, (tsize_t) -1);
printf("p\n");
printf("grestore $pageTop restore\n");
totalPages++;
}
#define GetPageNumber(tif) \
TIFFGetField(tif, TIFFTAG_PAGENUMBER, &pn, &ptotal)
int
findPage(TIFF* tif, int pageNumber)
{
uint16 pn = (uint16) -1;
uint16 ptotal = (uint16) -1;
if (GetPageNumber(tif)) {
while (pn != pageNumber && TIFFReadDirectory(tif) && GetPageNumber(tif))
;
return (pn == pageNumber);
} else
return (TIFFSetDirectory(tif, pageNumber-1));
}
void
fax2ps(TIFF* tif, int npages, int* pages, char* filename)
{
if (npages > 0) {
uint16 pn, ptotal;
int i;
if (!GetPageNumber(tif))
fprintf(stderr, "%s: No page numbers, counting directories.\n",
filename);
for (i = 0; i < npages; i++) {
if (findPage(tif, pages[i]))
printTIF(tif, pages[i]);
else
fprintf(stderr, "%s: No page number %d\n", filename, pages[i]);
}
} else {
int pageNumber = 1;
do
printTIF(tif, pageNumber++);
while (TIFFReadDirectory(tif));
}
}
#undef GetPageNumber
/*
* Create a special PostScript font for printing FAX documents. By taking
* advantage of the font-cacheing mechanism, a substantial speed-up in
* rendering time is realized.
*/
static void
emitFont(FILE* fd)
{
static const char* fontPrologue[] = {
"/newfont 10 dict def newfont begin /FontType 3 def /FontMatrix [1",
"0 0 1 0 0] def /FontBBox [0 0 512 1] def /Encoding 256 array def",
"0 1 31{Encoding exch /255 put}for 120 1 255{Encoding exch /255",
"put}for Encoding 37 /255 put Encoding 40 /255 put Encoding 41 /255",
"put Encoding 92 /255 put /count 0 def /ls{Encoding exch count 3",
"string cvs cvn put /count count 1 add def}def 32 1 36{ls}for",
"38 1 39{ls}for 42 1 91{ls}for 93 1 99{ls}for /count 100",
"def 100 1 119{ls}for /CharDict 5 dict def CharDict begin /white",
"{dup 255 eq{pop}{1 dict begin 100 sub neg 512 exch bitshift",
"/cw exch def cw 0 0 0 cw 1 setcachedevice end}ifelse}def /black",
"{dup 255 eq{pop}{1 dict begin 110 sub neg 512 exch bitshift",
"/cw exch def cw 0 0 0 cw 1 setcachedevice 0 0 moveto cw 0 rlineto",
"0 1 rlineto cw neg 0 rlineto closepath fill end}ifelse}def /numbuild",
"{dup 255 eq{pop}{6 0 0 0 6 1 setcachedevice 0 1 5{0 moveto",
"dup 32 and 32 eq{1 0 rlineto 0 1 rlineto -1 0 rlineto closepath",
"fill newpath}if 1 bitshift}for pop}ifelse}def /.notdef {}",
"def /255 {}def end /BuildChar{exch begin dup 110 ge{Encoding",
"exch get 3 string cvs cvi CharDict /black get}{dup 100 ge {Encoding",
"exch get 3 string cvs cvi CharDict /white get}{Encoding exch get",
"3 string cvs cvi CharDict /numbuild get}ifelse}ifelse exec end",
"}def end /Bitfont newfont definefont 1 scalefont setfont",
NULL
};
int i;
for (i = 0; fontPrologue[i] != NULL; i++)
fprintf(fd, "%s\n", fontPrologue[i]);
}
static int
pcompar(const void* va, const void* vb)
{
const int* pa = (const int*) va;
const int* pb = (const int*) vb;
return (*pa - *pb);
}
static void usage(int code);
int
main(int argc, char** argv)
{
extern int optind;
extern char* optarg;
int c, pageNumber;
int* pages = 0, npages = 0;
int dowarnings = 0; /* if 1, enable library warnings */
time_t t;
TIFF* tif;
while ((c = getopt(argc, argv, "l:p:x:y:W:H:wS")) != -1)
switch (c) {
case 'H': /* page height */
pageHeight = atof(optarg);
break;
case 'S': /* scale to page */
scaleToPage = 1;
break;
case 'W': /* page width */
pageWidth = atof(optarg);
break;
case 'p': /* print specific page */
pageNumber = atoi(optarg);
if (pageNumber < 1) {
fprintf(stderr, "%s: Invalid page number (must be > 0).\n",
optarg);
usage(-1);
}
if (pages)
pages = (int*) realloc((char*) pages, (npages+1)*sizeof (int));
else
pages = (int*) malloc(sizeof (int));
pages[npages++] = pageNumber;
break;
case 'w':
dowarnings = 1;
break;
case 'x':
defxres = atof(optarg);
break;
case 'y':
defyres = atof(optarg);
break;
case 'l':
maxline = atoi(optarg);
break;
case '?':
usage(-1);
}
if (npages > 0)
qsort(pages, npages, sizeof (int), pcompar);
if (!dowarnings)
TIFFSetWarningHandler(0);
printf("%%!PS-Adobe-3.0\n");
printf("%%%%Creator: fax2ps\n");
#ifdef notdef
printf("%%%%Title: %s\n", file);
#endif
t = time(0);
printf("%%%%CreationDate: %s", ctime(&t));
printf("%%%%Origin: 0 0\n");
printf("%%%%BoundingBox: 0 0 %u %u\n",
(int)(pageWidth*72), (int)(pageHeight*72)); /* XXX */
printf("%%%%Pages: (atend)\n");
printf("%%%%EndComments\n");
printf("%%%%BeginProlog\n");
emitFont(stdout);
printf("/d{bind def}def\n"); /* bind and def proc */
printf("/m{0 exch moveto}d\n");
printf("/s{show}d\n");
printf("/p{showpage}d \n"); /* end page */
printf("%%%%EndProlog\n");
if (optind < argc) {
do {
tif = TIFFOpen(argv[optind], "r");
if (tif) {
fax2ps(tif, npages, pages, argv[optind]);
TIFFClose(tif);
} else
fprintf(stderr, "%s: Can not open, or not a TIFF file.\n",
argv[optind]);
} while (++optind < argc);
} else {
int n;
FILE* fd;
char temp[1024], buf[16*1024];
strcpy(temp, "/tmp/fax2psXXXXXX");
(void) mktemp(temp);
fd = fopen(temp, "w");
if (fd == NULL) {
fprintf(stderr, "Could not create temp file \"%s\"\n", temp);
exit(-2);
}
while ((n = read(fileno(stdin), buf, sizeof (buf))) > 0)
write(fileno(fd), buf, n);
tif = TIFFOpen(temp, "r");
#ifndef VMS
unlink(temp);
#else
remove(temp);
#endif
if (tif) {
fax2ps(tif, npages, pages, "<stdin>");
TIFFClose(tif);
} else
fprintf(stderr, "%s: Can not open, or not a TIFF file.\n", temp);
fclose(fd);
}
printf("%%%%Trailer\n");
printf("%%%%Pages: %u\n", totalPages);
printf("%%%%EOF\n");
return (0);
}
char* stuff[] = {
"usage: fax2ps [options] [input.tif ...]",
"where options are:",
" -w suppress warning messages",
" -l chars set maximum output line length for generated PostScript",
" -p page# select page to print (can use multiple times)",
" -x xres set default horizontal resolution of input data (dpi)",
" -y yres set default vertical resolution of input data (lpi)",
" -S scale output to page size",
" -W width set output page width (inches), default is 8.5",
" -H height set output page height (inchest), default is 11",
NULL
};
static void
usage(int code)
{
char buf[BUFSIZ];
int i;
setbuf(stderr, buf);
for (i = 0; stuff[i] != NULL; i++)
fprintf(stderr, "%s\n", stuff[i]);
exit(code);
}