/* * pbmtextps.c - render text into a bitmap using a postscript interpreter * * Copyright (C) 2002 by James McCann. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. This software is provided "as is" without express or * implied warranty. * * PostScript is a registered trademark of Adobe Systems International. * */ #define _XOPEN_SOURCE /* Make sure popen() is in stdio.h */ #define _BSD_SOURCE /* Make sure stdrup() is in string.h */ #include #include #include #include "pbm.h" #include "nstring.h" #include "shhopt.h" #define FILENAME_LEN 200 static FILE *psfile = 0; char psfname[FILENAME_LEN]; static int pbmfile = 0; char pbmfname[FILENAME_LEN]; #define BUFFER_SIZE 2048 const char *gs_exe_path = #ifdef GHOSTSCRIPT_EXECUTABLE_PATH GHOSTSCRIPT_EXECUTABLE_PATH; #else 0; #endif const char *pnmcrop_exe_path = #ifdef PNMCROP_EXECUTABLE_PATH PNMCROP_EXECUTABLE_PATH; #else 0; #endif static struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ int res; /* resolution, DPI */ int fontsize; /* Size of font in points */ const char *font; /* Name of postscript font */ float stroke; /* Width of stroke in points (only for outline font) */ unsigned int verbose; const char *text; } cmdline; static void parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) { /*--------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -------------------------------------------------------------------------*/ optEntry *option_def = malloc(100*sizeof(optEntry)); /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; int i; char * text; int totaltextsize = 0; option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "resolution", OPT_INT, &cmdlineP->res, NULL, 0); OPTENT3(0, "font", OPT_STRING, &cmdlineP->font, NULL, 0); OPTENT3(0, "fontsize", OPT_INT, &cmdlineP->fontsize, NULL, 0); OPTENT3(0, "stroke", OPT_FLOAT, &cmdlineP->stroke, NULL, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); /* Set the defaults */ cmdlineP->res = 150; cmdlineP->fontsize = 24; cmdlineP->font = "Times-Roman"; cmdlineP->stroke = -1; opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; optParseOptions3(&argc, argv, opt, sizeof(opt), 0); text = NULL; for (i = 1; i < argc; i++) { if (i > 1) { totaltextsize += 1; text = realloc(text, totaltextsize); if (text == NULL) pm_error("out of memory"); strcat(text, " "); } totaltextsize += strlen(argv[i]); text = realloc(text, totaltextsize); if (text == NULL) pm_error("out of memory"); strcat(text, argv[i]); } cmdlineP->text = text; } static char * construct_postscript(struct cmdlineInfo *cmdl) { int size = 100; char *buffer = malloc (size); int nchars; const char *template; if(cmdl->stroke <= 0) { template ="/%s findfont\n%d scalefont\nsetfont\n12 36 moveto\n" "(%s) show\nshowpage\n"; } else { template ="/%s findfont\n%d scalefont\nsetfont\n12 36 moveto\n" "%f setlinewidth\n0 setgray\n" "(%s) true charpath\nstroke\nshowpage\n"; } nchars = snprintfN (buffer, size, template, cmdl->font, cmdl->fontsize, cmdl->text); if (nchars >= size) { if(!(buffer = realloc (buffer, nchars + 1))) pm_error("Can't allocate buffer for postscript string"); } if(cmdl->stroke < 0) snprintfN (buffer, nchars, template, cmdl->font, cmdl->fontsize, cmdl->text); else snprintfN (buffer, nchars, template, cmdl->font, cmdl->fontsize, cmdl->stroke, cmdl->text); return buffer; } static const char * gs_executable_name() { static char buffer[BUFFER_SIZE]; if(! gs_exe_path) { const char * const which = "which gs"; FILE *f; memset(buffer, 0, BUFFER_SIZE); if(!(f = popen(which, "r"))) pm_error("Can't find ghostscript"); fread(buffer, 1, BUFFER_SIZE, f); if(buffer[strlen(buffer) - 1] == '\n') buffer[strlen(buffer) - 1] = 0; pclose(f); if(buffer[0] != '/' && buffer[0] != '.') pm_error("Can't find ghostscript"); } else strcpy(buffer, gs_exe_path); return buffer; } static const char * crop_executable_name() { static char buffer[BUFFER_SIZE]; if(! pnmcrop_exe_path) { const char * const which = "which pnmcrop"; FILE *f; memset(buffer, 0, BUFFER_SIZE); if(!(f = popen(which, "r"))) { return 0; } fread(buffer, 1, BUFFER_SIZE, f); if(buffer[strlen(buffer) - 1] == '\n') buffer[strlen(buffer) - 1] = 0; pclose(f); if(buffer[0] != '/' && buffer[0] != '.') { buffer[0] = 0; pm_message("Can't find pnmcrop"); } } else strcpy(buffer, pnmcrop_exe_path); return buffer; } static char * gs_command(struct cmdlineInfo *cmdl) { static char com[BUFFER_SIZE]; int x = cmdl->res * 11; int y = cmdl->res * (cmdl->fontsize * 2 + 72) / 72.; snprintfN(com, BUFFER_SIZE, "%s -g%dx%d -r%d -sDEVICE=pbm " "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s /dev/null", gs_executable_name(), x, y, cmdl->res, pbmfname, psfname); return com; } static char * crop_command(void) { static char com[BUFFER_SIZE]; if(crop_executable_name()) snprintfN(com, BUFFER_SIZE, "%s -top -right %s", crop_executable_name(), pbmfname); else return 0; return com; } static void cleanup(void) { if(psfile) { fclose(psfile); unlink(psfname); } if(pbmfile) { unlink(pbmfname); } } static void create_outputfile(struct cmdlineInfo *cmdl) { char *ps; char *com; const char *template = "./pstextpbm.%d.tmp.%s"; FILE *pnmcrop; sprintf(psfname, template, getpid(), "ps"); if(! (psfile = fopen(psfname, "w"))) pm_error("Can't open temp file"); sprintf(pbmfname, template, getpid(), "pbm"); pbmfile = 1; if(! (com = strdup(gs_command(cmdl)))) pm_error("Can't allocate ghostscript command"); ps = construct_postscript(cmdl); if (cmdl->verbose) pm_message("Postscript program = '%s'", ps); if(fwrite(ps, 1, strlen(ps), psfile) != strlen(ps)) pm_error("Can't write postscript to temp file"); fclose(psfile); if (cmdl->verbose) pm_message("Running Postscript interpreter '%s'", com); if(system(com)) pm_error("Can't run ghostscript process"); free(com); com = crop_command(); if(com) { if (cmdl->verbose) pm_message("Running crop command '%s'", com); if(!(pnmcrop= popen(com, "r"))) pm_error("Can't run pnmcrop process"); else { char buf[2048]; int chars; while( (chars = fread(buf, 1, 2048, pnmcrop) ) == 2048) { if(fwrite(buf, 1, chars, stdout) != chars) pm_error("Can't write to stdout"); } if(chars < 0) pm_error("Can't read from pbm file"); if(chars > 0) if(fwrite(buf, 1, chars, stdout) != chars) pm_error("Can't write to stdout"); fclose(pnmcrop); } } else /* If the crop command can't be found warn and keep going */ pm_message("Can't find pnmcrop command, image will be large"); } int main(int argc, char *argv[]) { pbm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); atexit(cleanup); create_outputfile(&cmdline); exit(0); }