417 lines
10 KiB
C
417 lines
10 KiB
C
/* Convert lab, cmyk and mono images to postscript.
|
|
*/
|
|
|
|
/*
|
|
|
|
This file is part of VIPS.
|
|
|
|
VIPS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include <vips/vips.h>
|
|
#include <vips/util.h>
|
|
|
|
#include "vips2dj.h"
|
|
|
|
static const char *argv0 = NULL;
|
|
|
|
/* Geometries for the printers we know about.
|
|
*/
|
|
PrinterGeometry printer_data[] = {
|
|
/* name, paper width, print width, print length, left, top */
|
|
{ "2500cp", 2592, 2502, 3728, 51, 82 },
|
|
{ "3500cp", 3888, 3786, 5212, 51, 82 },
|
|
{ "5000ps", 4320, 4280, 5212, 20, 99 },
|
|
{ "4600dn", 595, 570, 817, 11, 15 },
|
|
{ "4700n", 595, 569, 816, 17, 14 }
|
|
};
|
|
|
|
/* Print a geo entry.
|
|
*/
|
|
static void
|
|
print_printers( void )
|
|
{
|
|
int i;
|
|
|
|
printf( "%12s %12s %12s %12s %12s %12s\n", "printer name",
|
|
"paper width", "print width", "print length",
|
|
"left margin", "top margin" );
|
|
for( i = 0; i < IM_NUMBER( printer_data ); i++ )
|
|
printf( "%12s %12d %12d %12d %12d %12d\n",
|
|
printer_data[i].name,
|
|
printer_data[i].pwidth,
|
|
printer_data[i].width,
|
|
printer_data[i].length,
|
|
printer_data[i].left,
|
|
printer_data[i].top );
|
|
}
|
|
|
|
/* Turn a name to a printer geometry.
|
|
*/
|
|
static PrinterGeometry *
|
|
find_printer( char *name )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < IM_NUMBER( printer_data ); i++ )
|
|
if( strcmp( name, printer_data[i].name ) == 0 )
|
|
return( &printer_data[i] );
|
|
|
|
im_errormsg( "vips2dj: unknown printer \"%s\"", name );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Copy between two fds
|
|
*/
|
|
static int
|
|
copy_bytes( FILE *in, FILE *out )
|
|
{
|
|
int ch;
|
|
|
|
while( (ch = getc( in )) != EOF )
|
|
if( putc( ch, out ) == EOF ) {
|
|
im_errormsg( "vips2dj: write error -- disc full?" );
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Send a file to out. Used to transmit the preludes.
|
|
*/
|
|
static int
|
|
transmit_file( char *mode, char *name, FILE *out )
|
|
{
|
|
const char *prefix;
|
|
char buf[PATH_MAX];
|
|
FILE *in;
|
|
|
|
if( !(prefix = im_guess_prefix( argv0, "VIPSHOME" )) )
|
|
return( -1 );
|
|
|
|
/* Send it!
|
|
*/
|
|
im_snprintf( buf, PATH_MAX, "%s/share/vips/vips2dj/%s/%s",
|
|
prefix, mode, name );
|
|
if( !(in = fopen( buf, "r" )) ) {
|
|
im_errormsg( "vips2dj: can't find \"%s\"", name );
|
|
return( -1 );
|
|
}
|
|
if( copy_bytes( in, out ) ) {
|
|
fclose( in );
|
|
return( -1 );
|
|
}
|
|
fclose( in );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Send the file to fp. width and height are the size to print at in points.
|
|
*/
|
|
static int
|
|
send_file( PrinterGeometry *geo, IMAGE *im, char *mode,
|
|
FILE *out, int width, int height )
|
|
{
|
|
/* Send all the start stuff.
|
|
*/
|
|
if( transmit_file( mode, "head1", out ) )
|
|
return( -1 );
|
|
|
|
/* Set page size.
|
|
*/
|
|
fprintf( out, "<</PageSize[%d %d]/ImagingBBox null>>setpagedevice\n",
|
|
geo->pwidth, height + 2*geo->top );
|
|
|
|
if( transmit_file( mode, "head2", out ) )
|
|
return( -1 );
|
|
|
|
/* Set mT (margin transform? don't know)
|
|
*/
|
|
fprintf( out, "/mT[1 0 0 -1 %d %d]def\n",
|
|
geo->left, height + geo->top );
|
|
|
|
if( transmit_file( mode, "head3", out ) )
|
|
return( -1 );
|
|
|
|
/* Set rC ... printable area.
|
|
*/
|
|
fprintf( out, "gS 0 0 %d %d rC\n", width, height );
|
|
|
|
if( transmit_file( mode, "head4", out ) )
|
|
return( -1 );
|
|
|
|
/* Set image params.
|
|
*/
|
|
fprintf( out, "/rows %d def\n", im->Ysize );
|
|
fprintf( out, "/cols %d def\n", im->Xsize );
|
|
fprintf( out, "%d %d scale\n", width, height );
|
|
|
|
if( transmit_file( mode, "head5", out ) )
|
|
return( -1 );
|
|
|
|
/* Send the body of the image.
|
|
*/
|
|
if( vips2asciihex( im, out ) )
|
|
return( -1 );
|
|
|
|
if( transmit_file( mode, "head6", out ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Start here!
|
|
*/
|
|
int
|
|
main( int argc, char **argv )
|
|
{
|
|
IMAGE *im = NULL;
|
|
FILE *out = stdout;
|
|
int width = -1;
|
|
int height = -1;
|
|
int dpi = -1;
|
|
int max = 0;
|
|
int rotate = 0;
|
|
int one2one = 0;
|
|
PrinterGeometry *geo = find_printer( "2500cp" );
|
|
char *mode;
|
|
int i;
|
|
|
|
if( im_init_world( argv[0] ) )
|
|
error_exit( "unable to start VIPS" );
|
|
|
|
argv0 = argv[0];
|
|
|
|
if( argc <= 1 ) {
|
|
printf(
|
|
"usage:\n"
|
|
"\t%s [options] <image file>\n"
|
|
"convert RGB, LAB, CMYK and mono image files to postscript\n"
|
|
"\tRGB converted to LAB, assuming sRGB\n"
|
|
"\tLAB printed with printer colour management\n"
|
|
"\tCMYK sent directly as dot percent\n"
|
|
"\tmono prints as K only\n"
|
|
"options include:\n"
|
|
"\t-printer <name>\tformat for printer <name>\n"
|
|
"\t-3500cp\t\tfor HP 3500CP printer (default 2500cp)\n"
|
|
"\t-max\t\tprint as large as possible\n"
|
|
"\t-rotate\t\trotate, if necessary, to fill the page\n"
|
|
"\t-1:1\t\tsize the image to print at 1:1 ... resolution in\n"
|
|
"\t\t\timage header must be set for this\n"
|
|
"\t-width <n>\tforce specified width, in points\n"
|
|
"\t-height <n>\tforce specified height, in points\n"
|
|
"\t-dpi <n>\tforce specified resolution (default 150dpi)\n"
|
|
"\t-a5, -a4, -a3, -a2, -a1, -a0\n"
|
|
"\t\t\tforce specified height (width ignored)\n"
|
|
"\t-o <file>\toutput to file (default stdout)\n",
|
|
argv0 );
|
|
printf( "supported printers:\n" );
|
|
print_printers();
|
|
return( 1 );
|
|
}
|
|
|
|
/* Decode args .. just look for file names and our three options.
|
|
*/
|
|
for( i = 1; i < argc; i++ )
|
|
if( *argv[i] == '-' ) {
|
|
if( strcmp( argv[i]+1, "width" ) == 0 ) {
|
|
if( !argv[i+1] || sscanf( argv[i+1],
|
|
"%d", &width ) != 1 || width <= 10 )
|
|
error_exit( "bad width" );
|
|
i++;
|
|
}
|
|
else if( strcmp( argv[i]+1, "height" ) == 0 ) {
|
|
if( !argv[i+1] || sscanf( argv[i+1],
|
|
"%d", &height ) != 1 || height <= 10 )
|
|
error_exit( "bad height" );
|
|
i++;
|
|
}
|
|
else if( strcmp( argv[i]+1, "3500cp" ) == 0 ) {
|
|
geo = find_printer( "3500cp" );
|
|
}
|
|
else if( strcmp( argv[i]+1, "printer" ) == 0 ) {
|
|
if( !argv[i+1] ||
|
|
!(geo = find_printer( argv[i+1] )) )
|
|
error_exit( "bad printer model" );
|
|
i++;
|
|
}
|
|
else if( strcmp( argv[i]+1, "dpi" ) == 0 ) {
|
|
if( !argv[i+1] || sscanf( argv[i+1],
|
|
"%d", &dpi ) != 1 || dpi <= 1 ||
|
|
dpi >= 600 )
|
|
error_exit( "bad dpi" );
|
|
i++;
|
|
}
|
|
else if( strcmp( argv[i]+1, "o" ) == 0 ) {
|
|
if( !argv[i+1] || !(out = fopen(
|
|
argv[i+1], "w" )) )
|
|
error_exit( "bad output name" );
|
|
i++;
|
|
}
|
|
else if( strcmp( argv[i]+1, "1:1" ) == 0 )
|
|
one2one = 1;
|
|
else if( strcmp( argv[i]+1, "a5" ) == 0 )
|
|
height = 595;
|
|
else if( strcmp( argv[i]+1, "a4" ) == 0 )
|
|
height = 839;
|
|
else if( strcmp( argv[i]+1, "a3" ) == 0 )
|
|
height = 1187;
|
|
else if( strcmp( argv[i]+1, "a2" ) == 0 )
|
|
height = 1678;
|
|
else if( strcmp( argv[i]+1, "a1" ) == 0 )
|
|
height = 2373;
|
|
else if( strcmp( argv[i]+1, "a0" ) == 0 )
|
|
height = 3356;
|
|
else if( strcmp( argv[i]+1, "max" ) == 0 )
|
|
max = 1;
|
|
else if( strcmp( argv[i]+1, "rotate" ) == 0 )
|
|
rotate = 1;
|
|
else
|
|
error_exit( "bad flag" );
|
|
}
|
|
else {
|
|
/* Try to open the file.
|
|
*/
|
|
if( im != NULL || !(im = im_open( argv[i], "r" )) )
|
|
error_exit( "bad input image" );
|
|
}
|
|
|
|
if( im == NULL )
|
|
error_exit( "no input image" );
|
|
|
|
/* Turn 3-band uchar images into LABQ. Yuk! But convenient.
|
|
*/
|
|
if( im->Coding == IM_CODING_NONE &&
|
|
im->Bands == 3 && im->BandFmt == IM_BANDFMT_UCHAR ) {
|
|
IMAGE *t[3];
|
|
|
|
if( im_open_local_array( im, t, 3, "vips2dj", "p" ) ||
|
|
im_sRGB2XYZ( im, t[0] ) ||
|
|
im_XYZ2Lab( t[0], t[1] ) ||
|
|
im_Lab2LabQ( t[1], t[2] ) )
|
|
error_exit( "error converting to LAB" );
|
|
|
|
im = t[2];
|
|
}
|
|
|
|
/* Stop used-before-set complaints on mode.
|
|
*/
|
|
mode = "lab";
|
|
|
|
/* Pick a PS mode.
|
|
*/
|
|
if( im->Coding == IM_CODING_LABQ )
|
|
mode = "lab";
|
|
else if( im->Coding == IM_CODING_NONE &&
|
|
im->Bands == 4 && im->BandFmt == IM_BANDFMT_UCHAR )
|
|
mode = "cmyk";
|
|
else if( im->Coding == IM_CODING_NONE &&
|
|
im->Bands == 1 && im->BandFmt == IM_BANDFMT_UCHAR )
|
|
mode = "mono";
|
|
else
|
|
error_exit( "unsupported image type "
|
|
"(IM_CODING_LABQ, mono, IM_TYPE_CMYK only)" );
|
|
|
|
/* Autorotate image to fill the page. We ought to get PS to do the
|
|
* rotate, really.
|
|
*/
|
|
if( rotate ) {
|
|
float iaspect = (float) im->Xsize / im->Ysize;
|
|
float paspect = (float) geo->width / geo->length;
|
|
|
|
if( iaspect > paspect ) {
|
|
IMAGE *t[1];
|
|
|
|
if( im_open_local_array( im, t, 1, "vips2dj", "p" ) ||
|
|
im_rot90( im, t[0] ) )
|
|
error_exit( "error rotating" );
|
|
|
|
im = t[0];
|
|
}
|
|
}
|
|
|
|
/* Make sure width and height are both set.
|
|
*/
|
|
if( one2one ) {
|
|
/* Set width/height from res.
|
|
*/
|
|
if( im->Xres <= 0 || im->Xres >= 100 ||
|
|
im->Yres <= 0 || im->Yres >= 100 )
|
|
error_exit( "uanble to print 1:1 - resolution not "
|
|
"set in image" );
|
|
|
|
height = (((im->Ysize / im->Yres) / 10.0) / 2.54) * 72.0;
|
|
width = (((im->Xsize / im->Xres) / 10.0) / 2.54) * 72.0;
|
|
}
|
|
else if( max ) {
|
|
float iaspect = (float) im->Xsize / im->Ysize;
|
|
float paspect = (float) geo->width / geo->length;
|
|
|
|
if( iaspect > paspect )
|
|
/* Image aspect ratio > paper ... fit width.
|
|
*/
|
|
width = geo->width;
|
|
else
|
|
height = geo->length;
|
|
}
|
|
else if( dpi > 0 ) {
|
|
/* Given res ... set width/height.
|
|
*/
|
|
height = (im->Ysize / (float) dpi) * 72.0;
|
|
width = (im->Xsize / (float) dpi) * 72.0;
|
|
}
|
|
|
|
if( width >= 0 || height >= 0 ) {
|
|
/* Given width or height or both --- set other one.
|
|
*/
|
|
if( height < 0 ) {
|
|
float fdpi = im->Xsize / (width / 72.0);
|
|
height = (im->Ysize / fdpi) * 72.0;
|
|
}
|
|
else {
|
|
float fdpi = im->Ysize / (height / 72.0);
|
|
width = (im->Xsize / fdpi) * 72.0;
|
|
}
|
|
}
|
|
else {
|
|
/* Nothing set ... default to 150 dpi.
|
|
*/
|
|
height = (im->Ysize / 150.0) * 72.0;
|
|
width = (im->Xsize / 150.0) * 72.0;
|
|
}
|
|
|
|
if( send_file( geo, im, mode, out, width, height ) )
|
|
error_exit( "error sending file" );
|
|
|
|
return( 0 );
|
|
}
|