diff options
Diffstat (limited to 'contrib/groff/src/preproc/html/pre-html.cpp')
-rw-r--r-- | contrib/groff/src/preproc/html/pre-html.cpp | 1530 |
1 files changed, 1530 insertions, 0 deletions
diff --git a/contrib/groff/src/preproc/html/pre-html.cpp b/contrib/groff/src/preproc/html/pre-html.cpp new file mode 100644 index 0000000..8869136 --- /dev/null +++ b/contrib/groff/src/preproc/html/pre-html.cpp @@ -0,0 +1,1530 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Written by Gaius Mulley (gaius@glam.ac.uk). + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff 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 General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define PREHTMLC + +#include "lib.h" + +#include <signal.h> +#include <ctype.h> +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include "errarg.h" +#include "error.h" +#include "stringclass.h" +#include "posix.h" +#include "defs.h" +#include "searchpath.h" +#include "paper.h" +#include "font.h" + +#include <errno.h> +#include <sys/types.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef _POSIX_VERSION +#include <sys/wait.h> +#define PID_T pid_t +#else /* not _POSIX_VERSION */ +#define PID_T int +#endif /* not _POSIX_VERSION */ + +#include <stdarg.h> + +#include "nonposix.h" + +extern "C" const char *Version_string; + +#include "pre-html.h" +#include "pushback.h" +#include "html-strings.h" + +#define DEFAULT_LINE_LENGTH 7 // inches wide +#define DEFAULT_IMAGE_RES 100 // number of pixels per inch resolution +#define IMAGE_BOARDER_PIXELS 0 +#define INLINE_LEADER_CHAR '\\' + +#define TRANSPARENT "-background white -transparent white" +#define MIN_ALPHA_BITS 0 +#define MAX_ALPHA_BITS 4 + +#define PAGE_TEMPLATE_SHORT "pg" +#define PAGE_TEMPLATE_LONG "-page-" +#define PS_TEMPLATE_SHORT "ps" +#define PS_TEMPLATE_LONG "-ps-" +#define REGION_TEMPLATE_SHORT "rg" +#define REGION_TEMPLATE_LONG "-regions-" + +#if 0 +# define DEBUGGING +#endif + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +typedef enum {CENTERED, LEFT, RIGHT, INLINE} IMAGE_ALIGNMENT; + +static int postscriptRes =-1; // postscript resolution, dots per inch +static int stdoutfd = 1; // output file descriptor - normally 1 but might move + // -1 means closed +static int copyofstdoutfd =-1; // a copy of stdout, so we can restore stdout when + // writing to post-html +static char *psFileName = NULL; // name of postscript file +static char *psPageName = NULL; // name of file containing postscript current page +static char *regionFileName = NULL; // name of file containing all image regions +static char *imagePageName = NULL; // name of bitmap image containing current page +static const char *image_device = "pnmraw"; +static int image_res = DEFAULT_IMAGE_RES; +static int vertical_offset = 0; +static char *image_template = NULL; // image template filename +static char *macroset_template= NULL; // image template passed to troff by -D +static int troff_arg = 0; // troff arg index +static char *image_dir = NULL; // user specified image directory +static int textAlphaBits = MAX_ALPHA_BITS; +static int graphicAlphaBits = MAX_ALPHA_BITS; +static char *antiAlias = NULL; // antialias arguments we pass to gs. +static int show_progress = FALSE; // should we display page numbers as they are processed? +static int currentPageNo = -1; // current image page number +#if defined(DEBUGGING) +static int debug = FALSE; +static char *troffFileName = NULL; // output of pre-html output which is sent to troff -Tps +static char *htmlFileName = NULL; // output of pre-html output which is sent to troff -Thtml +#endif + +static char *linebuf = NULL; // for scanning devps/DESC +static int linebufsize = 0; + +const char *const FONT_ENV_VAR = "GROFF_FONT_PATH"; +static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0); + + +/* + * Images are generated via postscript, gs and the pnm utilities. + */ + +#define IMAGE_DEVICE "-Tps" + +/* + * prototypes + */ +static int do_file(const char *filename); + +/* + * sys_fatal - writes a fatal error message. + * Taken from src/roff/groff/pipeline.c. + */ + +void sys_fatal (const char *s) +{ + fatal("%1: %2", s, strerror(errno)); +} + +/* + * get_line - copies a line (w/o newline) from a file to the global line buffer + */ + +int get_line (FILE *f) +{ + if (f == 0) + return 0; + if (linebuf == 0) { + linebuf = new char[128]; + linebufsize = 128; + } + int i = 0; + // skip leading whitespace + for (;;) { + int c = getc(f); + if (c == EOF) + return 0; + if (c != ' ' && c != '\t') { + ungetc(c, f); + break; + } + } + for (;;) { + int c = getc(f); + if (c == EOF) + break; + if (i + 1 >= linebufsize) { + char *old_linebuf = linebuf; + linebuf = new char[linebufsize * 2]; + memcpy(linebuf, old_linebuf, linebufsize); + a_delete old_linebuf; + linebufsize *= 2; + } + linebuf[i++] = c; + if (c == '\n') { + i--; + break; + } + } + linebuf[i] = '\0'; + return 1; +} + +/* + * get_resolution - returns the postscript resolution from devps/DESC + */ + +static unsigned int get_resolution (void) +{ + char *pathp; + FILE *f; + unsigned int res; + f = font_path.open_file("devps/DESC", &pathp); + a_delete pathp; + if (f == 0) + fatal("can't open devps/DESC"); + while (get_line(f)) { + int n = sscanf(linebuf, "res %u", &res); + if (n >= 1) { + fclose(f); + return res; + } + } + fatal("can't find `res' keyword in devps/DESC"); + return 0; +} + +/* + * html_system - a wrapper for system() + */ + +void html_system(const char *s, int redirect_stdout) +{ + // Redirect standard error to the null device. This is more + // portable than using "2> /dev/null", since it doesn't require a + // Unixy shell. + int save_stderr = dup(2); + int save_stdout = dup(1); + int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666); + if (save_stderr > 2 && fdnull > 2) + dup2(fdnull, 2); + if (redirect_stdout && save_stdout > 1 && fdnull > 1) + dup2(fdnull, 1); + if (fdnull >= 0) + close(fdnull); + int status = system(s); + dup2(save_stderr, 2); + if (redirect_stdout) + dup2(save_stdout, 1); + if (status == -1) + fprintf(stderr, "Calling `%s' failed\n", s); + else if (status) + fprintf(stderr, "Calling `%s' returned status %d\n", s, status); + close(save_stderr); + close(save_stdout); +} + +/* + * make_message - taken from man printf(3), creates a string via malloc + * and places the result of the va args into string. + * Finally the new string is returned. + */ + +char * +make_message (const char *fmt, ...) +{ + /* Guess we need no more than 100 bytes. */ + int n, size = 100; + char *p; + char *np; + va_list ap; + if ((p = (char *)malloc (size)) == NULL) + return NULL; + while (1) { + /* Try to print in the allocated space. */ + va_start(ap, fmt); + n = vsnprintf (p, size, fmt, ap); + va_end(ap); + /* If that worked, return the string. */ + if (n > -1 && n < size) { + if (size > n+1) { + np = strsave(p); + free(p); + return np; + } + return p; + } + /* Else try again with more space. */ + if (n > -1) /* glibc 2.1 */ + size = n+1; /* precisely what is needed */ + else /* glibc 2.0 */ + size *= 2; /* twice the old size */ + if ((np = (char *)realloc (p, size)) == NULL) { + free(p); /* realloc failed, free old, p. */ + return NULL; + } + p = np; /* use realloc'ed, p */ + } +} + +/* + * the class and methods for retaining ascii text + */ + +struct char_block { + enum { SIZE = 256 }; + char buffer[SIZE]; + int used; + char_block *next; + + char_block(); +}; + +/* + * char_block - constructor, sets the, used, and, next, fields to zero. + */ + +char_block::char_block() +: used(0), next(0) +{ + for (int i = 0; i < SIZE; i++) + buffer[i] = 0; +} + +class char_buffer { +public: + char_buffer(); + ~char_buffer(); + int read_file(FILE *fp); + int do_html(int argc, char *argv[]); + int do_image(int argc, char *argv[]); + void write_file_html(void); + void write_file_troff(void); + void write_upto_newline (char_block **t, int *i, int is_html); + int can_see(char_block **t, int *i, const char *string); + int skip_spaces(char_block **t, int *i); + void skip_until_newline(char_block **t, int *i); +private: + char_block *head; + char_block *tail; +}; + +/* + * char_buffer - constructor + */ + +char_buffer::char_buffer() +: head(0), tail(0) +{ +} + +/* + * char_buffer - deconstructor, throws aways the whole buffer list. + */ + +char_buffer::~char_buffer() +{ + while (head != NULL) { + char_block *temp = head; + head = head->next; + delete temp; + } +} + +/* + * read_file - read in a complete file, fp, placing the contents inside char_blocks. + */ + +int char_buffer::read_file (FILE *fp) +{ + int n; + + while (! feof(fp)) { + if (tail == NULL) { + tail = new char_block; + head = tail; + } else { + if (tail->used == char_block::SIZE) { + tail->next = new char_block; + tail = tail->next; + } + } + // at this point we have a tail which is ready for the next SIZE bytes of the file + + n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp); + if (n <= 0) { + // error + return( 0 ); + } else { + tail->used += n*sizeof(char); + } + } + return( 1 ); +} + +/* + * writeNbytes - writes n bytes to stdout. + */ + +static void writeNbytes (const char *s, int l) +{ + int n=0; + int r; + + while (n<l) { + r = write(stdoutfd, s, l-n); + if (r<0) { + sys_fatal("write"); + } + n += r; + s += r; + } +} + +/* + * writeString - writes a string to stdout. + */ + +static void writeString (const char *s) +{ + writeNbytes(s, strlen(s)); +} + +/* + * makeFileName - creates the image filename template + * and the macroset image template. + */ + +static void makeFileName (void) +{ + if ((image_dir != NULL) && (strchr(image_dir, '%') != NULL)) { + error("cannot use a `%%' within the image directory name"); + exit(1); + } + + if ((image_template != NULL) && (strchr(image_template, '%') != NULL)) { + error("cannot use a `%%' within the image template"); + exit(1); + } + + if (image_dir == NULL) { + image_dir = ""; + } else if ((strlen(image_dir)>0) && (image_dir[strlen(image_dir)-1] != '/')) { + image_dir = make_message("%s/", image_dir); + if (image_dir == NULL) + sys_fatal("make_message"); + } + + if (image_template == NULL) + macroset_template = make_message("%sgrohtml-%d", image_dir, (int)getpid()); + else + macroset_template = make_message("%s%s", image_dir, image_template); + + if (macroset_template == NULL) + sys_fatal("make_message"); + + image_template = (char *)malloc(strlen("-%d")+strlen(macroset_template)+1); + if (image_template == NULL) + sys_fatal("malloc"); + strcpy(image_template, macroset_template); + strcat(image_template, "-%d"); +} + +/* + * setupAntiAlias - sets up the antialias string, used when we call gs. + */ + +static void setupAntiAlias (void) +{ + if (textAlphaBits == 0 && graphicAlphaBits == 0) + antiAlias = make_message(" "); + else if (textAlphaBits == 0) + antiAlias = make_message("-dGraphicsAlphaBits=%d ", graphicAlphaBits); + else if (graphicAlphaBits == 0) + antiAlias = make_message("-dTextAlphaBits=%d ", textAlphaBits); + else + antiAlias = make_message("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d ", + textAlphaBits, graphicAlphaBits); +} + +/* + * checkImageDir - checks to see whether the image directory is available. + */ + +static void checkImageDir (void) +{ + if ((image_dir != NULL) && (strcmp(image_dir, "") != 0)) + if (! ((mkdir(image_dir, 0777) == 0) || (errno == EEXIST))) { + error("cannot create directory `%1'", image_dir); + exit(1); + } +} + +/* + * write_end_image - ends the image. It writes out the image extents if we are using -Tps. + */ + +static void write_end_image (int is_html) +{ + /* + * if we are producing html then these + * emit image name and enable output + * else + * we are producing images + * in which case these generate image + * boundaries + */ + writeString("\\O[4]\\O[2]"); + if (is_html) + writeString("\\O[1]"); + else + writeString("\\O[0]"); +} + +/* + * write_start_image - writes the troff which will: + * + * (i) disable html output for the following image + * (ii) reset the max/min x/y registers during postscript + * rendering. + */ + +static void write_start_image (IMAGE_ALIGNMENT pos, int is_html) +{ + writeString("\\O[5"); + switch (pos) { + + case INLINE: + writeString("i"); + break; + case LEFT: + writeString("l"); + break; + case RIGHT: + writeString("r"); + break; + case CENTERED: + default: + writeString("c"); + break; + } + writeString(image_template); writeString(".png]"); + if (is_html) + writeString("\\O[0]\\O[3]"); + else + // reset min/max registers + writeString("\\O[1]\\O[3]"); +} + +/* + * write_upto_newline - writes the contents of the buffer until a newline is seen. + * It checks for HTML_IMAGE_INLINE_BEGIN and HTML_IMAGE_INLINE_END + * and if they are present it processes them. + */ + +void char_buffer::write_upto_newline (char_block **t, int *i, int is_html) +{ + int j=*i; + + if (*t) { + while ((j < (*t)->used) && ((*t)->buffer[j] != '\n') && + ((*t)->buffer[j] != INLINE_LEADER_CHAR)) { + j++; + } + if ((j < (*t)->used) && ((*t)->buffer[j] == '\n')) { + j++; + } + writeNbytes((*t)->buffer+(*i), j-(*i)); + if ((*t)->buffer[j] == INLINE_LEADER_CHAR) { + if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN)) + write_start_image(INLINE, is_html); + else if (can_see(t, &j, HTML_IMAGE_INLINE_END)) + write_end_image(is_html); + else { + if (j < (*t)->used) { + *i = j; + j++; + writeNbytes((*t)->buffer+(*i), j-(*i)); + } + } + } + if (j == (*t)->used) { + *i = 0; + if ((*t)->buffer[j-1] == '\n') { + *t = (*t)->next; + } else { + *t = (*t)->next; + write_upto_newline(t, i, is_html); + } + } else { + // newline was seen + *i = j; + } + } +} + +/* + * can_see - returns TRUE if we can see string in t->buffer[i] onwards + */ + +int char_buffer::can_see (char_block **t, int *i, const char *string) +{ + int j = 0; + int l = strlen(string); + int k = *i; + char_block *s = *t; + + while (s) { + while ((k<s->used) && (j<l) && (s->buffer[k] == string[j])) { + j++; + k++; + } + if (j == l) { + *i = k; + *t = s; + return( TRUE ); + } else if ((k<s->used) && (s->buffer[k] != string[j])) { + return( FALSE ); + } + s = s->next; + k = 0; + } + return( FALSE ); +} + +/* + * skip_spaces - returns TRUE if we have not run out of data. + * It also consumes spaces. + */ + +int char_buffer::skip_spaces(char_block **t, int *i) +{ + char_block *s = *t; + int k = *i; + + while (s) { + while ((k<s->used) && (isspace(s->buffer[k]))) { + k++; + } + if (k == s->used) { + k = 0; + s = s->next; + } else { + *i = k; + return( TRUE ); + } + } + return( FALSE ); +} + +/* + * skip_until_newline - skips all characters until a newline is seen. + * The newline is not consumed. + */ + +void char_buffer::skip_until_newline (char_block **t, int *i) +{ + int j=*i; + + if (*t) { + while ((j < (*t)->used) && ((*t)->buffer[j] != '\n')) { + j++; + } + if (j == (*t)->used) { + *i = 0; + *t = (*t)->next; + skip_until_newline(t, i); + } else { + // newline was seen + *i = j; + } + } +} + +/* + * write_file_troff - writes the buffer to stdout (troff). + */ + +void char_buffer::write_file_troff (void) +{ + char_block *t=head; + int i=0; + + if (t != NULL) { + do { + write_upto_newline(&t, &i, FALSE); + } while (t != NULL); + } + if (close(stdoutfd) < 0) + sys_fatal("close"); + + // now we grab fd=1 so that the next pipe cannot use fd=1 + if (stdoutfd == 1) { + if (dup(2) != stdoutfd) { + sys_fatal("dup failed to use fd=1"); + } + } +} + +/* + * the image class remembers the position of all images in the postscript file + * and assigns names for each image. + */ + +struct imageItem { + imageItem *next; + int X1; + int Y1; + int X2; + int Y2; + char *imageName; + int resolution; + int maxx; + int pageNo; + + imageItem (int x1, int y1, int x2, int y2, int page, int res, int max_width, char *name); + ~imageItem (); +}; + +/* + * imageItem - constructor + */ + +imageItem::imageItem (int x1, int y1, int x2, int y2, int page, int res, int max_width, char *name) +{ + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + pageNo = page; + resolution = res; + maxx = max_width; + imageName = name; + next = NULL; +} + +/* + * imageItem - deconstructor + */ + +imageItem::~imageItem () +{ + if (imageName) + free(imageName); +} + +/* + * imageList - class containing a list of imageItems. + */ + +class imageList { +private: + imageItem *head; + imageItem *tail; + int count; +public: + imageList(); + ~imageList(); + void add(int x1, int y1, int x2, int y2, int page, int res, int maxx, char *name); + void createImages (void); + int createPage (int pageno); + void createImage (imageItem *i); + int getMaxX (int pageno); +}; + +/* + * imageList - constructor. + */ + +imageList::imageList () + : head(0), tail(0), count(0) +{ +} + +/* + * imageList - deconstructor. + */ + +imageList::~imageList () +{ + while (head != NULL) { + imageItem *i = head; + head = head->next; + delete i; + } +} + +/* + * createPage - creates one image of, page pageno, from the postscript file. + */ + +int imageList::createPage (int pageno) +{ + char *s; + + if (currentPageNo == pageno) + return 0; + + if (currentPageNo >= 1) { + /* + * we need to unlink the files which change each time a new page is processed. + * The final unlink is done by xtmpfile when pre-grohtml exits. + */ + unlink(imagePageName); + unlink(psPageName); + } + + if (show_progress) { + fprintf(stderr, "[%d] ", pageno); + fflush(stderr); + } + +#if defined(DEBUGGING) + if (debug) + fprintf(stderr, "creating page %d\n", pageno); +#endif + + s = make_message("psselect -q -p%d %s %s\n", + pageno, psFileName, psPageName); + + if (s == NULL) + sys_fatal("make_message"); +#if defined(DEBUGGING) + if (debug) { + fwrite(s, sizeof(char), strlen(s), stderr); + fflush(stderr); + } +#endif + html_system(s, 1); + + s = make_message("echo showpage | " + "gs%s -q -dBATCH -dSAFER " + "-dDEVICEHEIGHTPOINTS=792 " + "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true " + "-sDEVICE=%s -r%d %s " + "-sOutputFile=%s %s -\n", + EXE_EXT, + (getMaxX(pageno) * image_res) / postscriptRes, + image_device, + image_res, + antiAlias, + imagePageName, + psPageName); + if (s == NULL) + sys_fatal("make_message"); +#if defined(DEBUGGING) + if (debug) { + fwrite(s, sizeof(char), strlen(s), stderr); + fflush(stderr); + } +#endif + html_system(s, 1); + free(s); + currentPageNo = pageno; + return 0; +} + +/* + * min - returns the minimum of two numbers. + */ + +int min (int x, int y) +{ + if (x < y) { + return( x ); + } else { + return( y ); + } +} + +/* + * max - returns the maximum of two numbers. + */ + +int max (int x, int y) +{ + if (x > y) { + return( x ); + } else { + return( y ); + } +} + +/* + * getMaxX - returns the largest right hand position for any image on, pageno + */ + +int imageList::getMaxX (int pageno) +{ + imageItem *h = head; + int x = postscriptRes * DEFAULT_LINE_LENGTH; + + while (h != NULL) { + if (h->pageNo == pageno) + x = max(h->X2, x); + h = h->next; + } + return x; +} + +/* + * createImage - generates a minimal png file from the set of page images. + */ + +void imageList::createImage (imageItem *i) +{ + if (i->X1 != -1) { + char *s; + int x1 = max(min(i->X1, i->X2)*image_res/postscriptRes-1*IMAGE_BOARDER_PIXELS, 0); + int y1 = max((image_res*vertical_offset/72)+min(i->Y1, i->Y2)*image_res/postscriptRes-IMAGE_BOARDER_PIXELS, 0); + int x2 = max(i->X1, i->X2)*image_res/postscriptRes+1*IMAGE_BOARDER_PIXELS; + int y2 = (image_res*vertical_offset/72)+(max(i->Y1, i->Y2)*image_res/postscriptRes)+1+IMAGE_BOARDER_PIXELS; + if (createPage(i->pageNo) == 0) { + s = make_message("pnmcut%s %d %d %d %d < %s | pnmcrop -quiet | pnmtopng%s %s > %s \n", + EXE_EXT, + x1, y1, x2-x1+1, y2-y1+1, + imagePageName, + EXE_EXT, + TRANSPARENT, + i->imageName); + if (s == NULL) + sys_fatal("make_message"); + +#if defined(DEBUGGING) + if (debug) { + fprintf(stderr, s); + fflush(stderr); + } +#endif + html_system(s, 0); + free(s); + } else { + fprintf(stderr, "failed to generate image of page %d\n", i->pageNo); + fflush(stderr); + } +#if defined(DEBUGGING) + } else { + if (debug) { + fprintf(stderr, "ignoring image as x1 coord is -1\n"); + fflush(stderr); + } +#endif + } +} + +/* + * add - an image description to the imageList. + */ + +void imageList::add (int x1, int y1, int x2, int y2, int page, int res, int maxx, char *name) +{ + imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name); + + if (head == NULL) { + head = i; + tail = i; + } else { + tail->next = i; + tail = i; + } +} + +/* + * createImages - foreach image descriptor on the imageList, create the actual image. + */ + +void imageList::createImages (void) +{ + imageItem *h = head; + + while (h != NULL) { + createImage(h); + h = h->next; + } +} + +static imageList listOfImages; // list of images defined by the region file. + +/* + * write_file_html - writes the buffer to stdout (troff). + * It writes out the file replacing template image names with + * actual image names. + */ + +void char_buffer::write_file_html (void) +{ + char_block *t=head; + int i=0; + + if (t != NULL) { + do { + write_upto_newline(&t, &i, TRUE); + } while (t != NULL); + } + if (close(stdoutfd) < 0) + sys_fatal("close"); + + // now we grab fd=1 so that the next pipe cannot use fd=1 + if (stdoutfd == 1) { + if (dup(2) != stdoutfd) { + sys_fatal("dup failed to use fd=1"); + } + } +} + +/* + * generateImages - parses the region file and generates images + * from the postscript file. The region file + * contains the x1,y1 x2,y2 extents of each + * image. + */ + +static void generateImages (char *regionFileName) +{ + pushBackBuffer *f=new pushBackBuffer(regionFileName); + + while (f->putPB(f->getPB()) != eof) { + if (f->isString("grohtml-info:page")) { + int page = f->readInt(); + int x1 = f->readInt(); + int y1 = f->readInt(); + int x2 = f->readInt(); + int y2 = f->readInt(); + int maxx = f->readInt(); + char *name = f->readString(); + int res = postscriptRes; + listOfImages.add(x1, y1, x2, y2, page, res, maxx, name); + while ((f->putPB(f->getPB()) != '\n') && + (f->putPB(f->getPB()) != eof)) { + (void)f->getPB(); + } + if (f->putPB(f->getPB()) == '\n') { + (void)f->getPB(); + } + } else { + /* + * write any error messages out to the user + */ + fputc(f->getPB(), stderr); + } + } + + listOfImages.createImages(); + if (show_progress) { + fprintf(stderr, "done\n"); + fflush(stderr); + } + delete f; +} + +/* + * replaceFd - replace a file descriptor, was, with, willbe. + */ + +static void replaceFd (int was, int willbe) +{ + int dupres; + + if (was != willbe) { + if (close(was)<0) { + sys_fatal("close"); + } + dupres = dup(willbe); + if (dupres != was) { + sys_fatal("dup"); + fprintf(stderr, "trying to replace fd=%d with %d dup used %d\n", was, willbe, dupres); + if (willbe == 1) { + fprintf(stderr, "likely that stdout should be opened before %d\n", was); + } + exit(1); + } + if (close(willbe) < 0) { + sys_fatal("close"); + } + } +} + +/* + * waitForChild - waits for child, pid, to exit. + */ + +static void waitForChild (PID_T pid) +{ + PID_T waitpd; + int status; + + waitpd = WAIT(&status, pid, _WAIT_CHILD); + if (waitpd != pid) + sys_fatal("wait"); +} + +/* + * alterDeviceTo - if toImage is set then the arg list is altered to include + * IMAGE_DEVICE and we invoke groff rather than troff. + * else + * set -Thtml and groff + */ + +static void alterDeviceTo (int argc, char *argv[], int toImage) +{ + int i=0; + + if (toImage) { + while (i < argc) { + if (strcmp(argv[i], "-Thtml") == 0) { + argv[i] = IMAGE_DEVICE; + } + i++; + } + argv[troff_arg] = "groff"; /* rather than troff */ + } else { + while (i < argc) { + if (strcmp(argv[i], IMAGE_DEVICE) == 0) { + argv[i] = "-Thtml"; + } + i++; + } + argv[troff_arg] = "groff"; /* use groff -Z */ + } +} + +/* + * addZ - appends -Z onto the command list for groff. + */ + +char **addZ (int argc, char *argv[]) +{ + char **new_argv = (char **)malloc((argc+2)*sizeof(char *)); + int i=0; + + if (new_argv == NULL) + sys_fatal("malloc"); + + if (argc > 0) { + new_argv[i] = argv[i]; + i++; + } + new_argv[i] = "-Z"; + while (i<argc) { + new_argv[i+1] = argv[i]; + i++; + } + argc++; + new_argv[argc] = NULL; + return new_argv; +} + +/* + * addRegDef - appends a defined register or string onto the command list for troff. + */ + +char **addRegDef (int argc, char *argv[], const char *numReg) +{ + char **new_argv = (char **)malloc((argc+2)*sizeof(char *)); + int i=0; + + if (new_argv == NULL) + sys_fatal("malloc"); + + while (i<argc) { + new_argv[i] = argv[i]; + i++; + } + new_argv[argc] = strsave(numReg); + argc++; + new_argv[argc] = NULL; + return new_argv; +} + +/* + * dump_args - display the argument list. + */ + +void dump_args (int argc, char *argv[]) +{ + fprintf(stderr, " %d arguments:", argc); + for (int i=0; i<argc; i++) + fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); +} + +/* + * do_html - sets the troff number htmlflip and + * writes out the buffer to troff -Thtml + */ + +int char_buffer::do_html(int argc, char *argv[]) +{ + int pdes[2]; + PID_T pid; + string s; + + alterDeviceTo(argc, argv, 0); + argv += troff_arg; // skip all arguments up to groff + argc -= troff_arg; + argv = addZ(argc, argv); + argc++; + + s = "-dwww-image-template="; + s += macroset_template; // do not combine these statements otherwise they will not work + s += '\0'; // the trailing '\0' is ignored + argv = addRegDef(argc, argv, s.contents()); + argc++; + + if (pipe(pdes) < 0) + sys_fatal("pipe"); + + pid = fork(); + if (pid < 0) + sys_fatal("fork"); + + if (pid == 0) { + // child + replaceFd(0, pdes[0]); + // close end we are not using + if (close(pdes[1])<0) + sys_fatal("close"); + replaceFd(1, copyofstdoutfd); // and restore stdout + + execvp(argv[0], argv); + error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); + fflush(stderr); /* just in case error() doesn't */ + exit(1); + } else { + // parent + +#if defined(DEBUGGING) + /* + * slight security risk so only enabled if compiled with defined(DEBUGGING) + */ + if (debug) { + replaceFd(1, creat(htmlFileName, S_IWUSR|S_IRUSR)); + write_file_html(); + } +#endif + replaceFd(1, pdes[1]); + // close end we are not using + if (close(pdes[0])<0) + sys_fatal("close"); + + write_file_html(); + waitForChild(pid); + } + return 0; +} + +/* + * do_image - writes out the buffer to troff -Tps + */ + +int char_buffer::do_image(int argc, char *argv[]) +{ + PID_T pid; + int pdes[2]; + string s; + + alterDeviceTo(argc, argv, 1); + argv += troff_arg; // skip all arguments up to troff/groff + argc -= troff_arg; + argv = addRegDef(argc, argv, "-rps4html=1"); + argc++; + + s = "-dwww-image-template="; + s += macroset_template; + s += '\0'; + argv = addRegDef(argc, argv, s.contents()); + argc++; + + // override local settings and produce a page size letter postscript file + argv = addRegDef(argc, argv, "-P-pletter"); + argc++; + + if (pipe(pdes) < 0) + sys_fatal("pipe"); + + pid = fork(); + if (pid == 0) { + // child + + int psFd = creat(psFileName, S_IWUSR|S_IRUSR); + int regionFd = creat(regionFileName, S_IWUSR|S_IRUSR); + + replaceFd(1, psFd); + replaceFd(0, pdes[0]); + replaceFd(2, regionFd); + + // close end we are not using + if (close(pdes[1])<0) + sys_fatal("close"); + + execvp(argv[0], argv); + error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); + fflush(stderr); /* just in case error() doesn't */ + exit(1); + } else { + // parent + +#if defined(DEBUGGING) + /* + * slight security risk so only enabled if compiled with defined(DEBUGGING) + */ + if (debug) { + replaceFd(1, creat(troffFileName, S_IWUSR|S_IRUSR)); + write_file_troff(); + } +#endif + replaceFd(1, pdes[1]); + write_file_troff(); + waitForChild(pid); + } + return 0; +} + +static char_buffer inputFile; + + +/* + * usage - emit usage arguments. + */ + +void usage(FILE *stream) +{ + fprintf(stream, "usage: %s troffname [-Iimage_name] [-Dimage_directory] [-P-o vertical_image_offset] [-P-i image_resolution] [troff flags] [files]\n", program_name); + fprintf(stream, " vertical_image_offset (default %d/72 of an inch)\n", vertical_offset); + fprintf(stream, " image_resolution (default %d) pixels per inch\n", image_res); + fprintf(stream, " image_name is the name of the stem for all images (default is grohtml-<pid>)\n"); + fprintf(stream, " place all png files into image_directory\n"); +} + +/* + * scanArguments - scans for all arguments including -P-i, -P-o, -P-D and -P-I. It returns + * the argument index of the first non option. + */ + +int scanArguments (int argc, char **argv) +{ + const char *command_prefix = getenv("GROFF_COMMAND_PREFIX"); + if (!command_prefix) + command_prefix = PROG_PREFIX; + char *troff_name = new char[strlen(command_prefix) + strlen("troff") + 1]; + strcpy(troff_name, command_prefix); + strcat(troff_name, "troff"); + int c, i; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, "+a:g:o:i:I:D:F:vbdhlrnp", long_options, NULL)) + != EOF) + switch(c) { + case 'v': + printf("GNU pre-grohtml (groff) version %s\n", Version_string); + exit(0); + case 'a': + textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)), MAX_ALPHA_BITS); + if (textAlphaBits == 3) { + error("cannot use 3 bits of antialiasing information"); + exit(1); + } + break; + case 'g': + graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)), MAX_ALPHA_BITS); + if (graphicAlphaBits == 3) { + error("cannot use 3 bits of antialiasing information"); + exit(1); + } + break; + case 'b': + // handled by post-grohtml (set background color to white) + break; + case 'D': + image_dir = optarg; + break; + case 'I': + image_template = optarg; + break; + case 'i': + image_res = atoi(optarg); + break; + case 'F': + font_path.command_line_dir(optarg); + break; + case 'o': + vertical_offset = atoi(optarg); + break; + case 'p': + show_progress = TRUE; + break; + case 'd': +#if defined(DEBUGGING) + debug = TRUE; +#endif + break; + case 'h': + // handled by post-grohtml + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + break; + } + + i = optind; + while (i < argc) { + if (strcmp(argv[i], troff_name) == 0) + troff_arg = i; + else if (argv[i][0] != '-') + return i; + i++; + } + a_delete troff_name; + + return argc; +} + +/* + * makeTempFiles - name the temporary files + */ + +static int makeTempFiles (void) +{ +#if defined(DEBUGGING) + psFileName = "/tmp/prehtml-ps"; + regionFileName = "/tmp/prehtml-region"; + imagePageName = "/tmp/prehtml-page"; + psPageName = "/tmp/prehtml-psn"; + troffFileName = "/tmp/prehtml-troff"; + htmlFileName = "/tmp/prehtml-html"; +#else + FILE *f; + + /* psPageName contains a single page of postscript */ + f = xtmpfile(&psPageName, + PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT, + TRUE); + if (f == NULL) { + sys_fatal("xtmpfile"); + return -1; + } + fclose(f); + + /* imagePageName contains a bitmap image of the single postscript page */ + f = xtmpfile(&imagePageName, + PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT, + TRUE); + if (f == NULL) { + sys_fatal("xtmpfile"); + return -1; + } + fclose(f); + + /* psFileName contains a postscript file of the complete document */ + f = xtmpfile(&psFileName, + PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT, + TRUE); + if (f == NULL) { + sys_fatal("xtmpfile"); + return -1; + } + fclose(f); + + /* regionFileName contains a list of the images and their boxed coordinates */ + f = xtmpfile(®ionFileName, + REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT, + TRUE); + if (f == NULL) { + sys_fatal("xtmpfile"); + return -1; + } + fclose(f); + +#endif + return 0; +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + int i; + int found=0; + int ok=1; + + postscriptRes = get_resolution(); + i = scanArguments(argc, argv); + setupAntiAlias(); + checkImageDir(); + makeFileName(); + while (i < argc) { + if (argv[i][0] != '-') { + /* found source file */ + ok = do_file(argv[i]); + if (! ok) { + return( 0 ); + } + found = 1; + } + i++; + } + + copyofstdoutfd=dup(stdoutfd); + + if (! found) { + do_file("-"); + } + if (makeTempFiles()) + return 1; + ok = inputFile.do_image(argc, argv); + if (ok == 0) { + generateImages(regionFileName); + ok = inputFile.do_html(argc, argv); + } + return ok; +} + +static int do_file(const char *filename) +{ + FILE *fp; + + current_filename = filename; + if (strcmp(filename, "-") == 0) { + fp = stdin; + } else { + fp = fopen(filename, "r"); + if (fp == 0) { + error("can't open `%1': %2", filename, strerror(errno)); + return 0; + } + } + + if (inputFile.read_file(fp)) { + } + + if (fp != stdin) + fclose(fp); + current_filename = NULL; + return 1; +} |