fonts for old html driver
[s-roff.git] / src / preproc / html2 / pre-html.cc
blobb282a8b7b10342ba5fd258ec77c31e41db443aff
1 // -*- C++ -*-
2 /* Copyright (C) 2000 Free Software Foundation, Inc.
3 Written by Gaius Mulley (gaius@glam.ac.uk).
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 #define PREHTMLC
23 #include <stdio.h>
24 #include <signal.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include "lib.h"
31 #include "errarg.h"
32 #include "error.h"
33 #include "stringclass.h"
34 #include "posix.h"
36 #include <errno.h>
37 #include <sys/types.h>
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
42 #ifdef _POSIX_VERSION
43 #include <sys/wait.h>
44 #define PID_T pid_t
45 #else /* not _POSIX_VERSION */
46 #define PID_T int
47 #endif /* not _POSIX_VERSION */
49 extern char *strerror();
51 #include "pre-html.h"
52 #include "pushbackbuffer.h"
54 #define POSTSCRIPTRES 72000 // maybe there is a better way to find this? --fixme--
55 #define DEFAULT_IMAGE_RES 80
56 #define IMAGE_BOARDER_PIXELS 10
58 // #define TRANSPARENT "-background \"#FFF\" -transparent \"#FFF\""
59 #define TRANSPARENT ""
61 #define DEBUGGING
63 #if !defined(TRUE)
64 # define TRUE (1==1)
65 #endif
66 #if !defined(FALSE)
67 # define FALSE (1==0)
68 #endif
70 void stop() {}
73 static int stdoutfd =1; // output file descriptor - normally 1 but might move
74 // -1 means closed
75 static char *psFileName =0; // name of postscript file
76 static char *regionFileName=0; // name of file containing all image regions
77 static char *image_device = "pnmraw";
78 static int image_res = DEFAULT_IMAGE_RES;
82 * Images are generated via postscript, gs and the pnm utilities.
85 #define IMAGEDEVICE "-Tps"
88 static int do_file(const char *filename);
91 * sys_fatal - writes a fatal error message. Taken from src/roff/groff/pipeline.c
94 void sys_fatal (const char *s)
96 fprintf(stderr, "%s: %s: %s", program_name, s, strerror(errno));
100 * the class and methods for retaining ascii text
103 struct char_block {
104 enum { SIZE = 256 };
105 char buffer[SIZE];
106 int used;
107 char_block *next;
109 char_block();
112 char_block::char_block()
113 : used(0), next(0)
117 class char_buffer {
118 public:
119 char_buffer();
120 ~char_buffer();
121 int read_file(FILE *fp);
122 int do_html(int argc, char *argv[]);
123 int do_image(int argc, char *argv[]);
124 void write_file_html(void);
125 void write_file_troff(void);
126 void write_upto_newline (char_block **t, int *i);
127 int can_see(char_block **t, int *i, char *string);
128 private:
129 char_block *head;
130 char_block *tail;
133 char_buffer::char_buffer()
134 : head(0), tail(0)
138 char_buffer::~char_buffer()
140 while (head != 0) {
141 char_block *temp = head;
142 head = head->next;
143 delete temp;
147 int char_buffer::read_file (FILE *fp)
149 int i=0;
150 unsigned int old_used;
151 int n;
153 while (! feof(fp)) {
154 if (tail == 0) {
155 tail = new char_block;
156 head = tail;
157 } else {
158 if (tail->used == char_block::SIZE) {
159 tail->next = new char_block;
160 tail = tail->next;
163 // at this point we have a tail which is ready for the next SIZE bytes of the file
165 n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp);
166 if (n <= 0) {
167 // error
168 return( 0 );
169 } else {
170 tail->used += n*sizeof(char);
173 return( 1 );
177 * writeNbytes - writes n bytes to stdout.
180 static void writeNbytes (char *s, int l)
182 int n=0;
183 int r;
185 while (n<l) {
186 r = write(stdoutfd, s, l-n);
187 if (r<0) {
188 sys_fatal("write");
190 n += r;
191 s += r;
196 * writeString - writes a string to stdout.
199 static void writeString (char *s)
201 writeNbytes(s, strlen(s));
205 * write_upto_newline - writes the contents of the buffer until a newline is seen.
208 void char_buffer::write_upto_newline (char_block **t, int *i)
210 int j=*i;
212 if (*t) {
213 while ((j < (*t)->used) && ((*t)->buffer[j] != '\n')) {
214 j++;
216 if ((j < (*t)->used) && ((*t)->buffer[j] == '\n')) {
217 j++;
219 writeNbytes((*t)->buffer+(*i), j-(*i));
220 if (j == (*t)->used) {
221 *i = 0;
222 *t = (*t)->next;
223 write_upto_newline(t, i);
224 } else {
225 // newline was seen
226 *i = j;
232 * can_see - returns TRUE if we can see string in t->buffer[i] onwards
235 int char_buffer::can_see(char_block **t, int *i, char *string)
237 int j = 0;
238 int l = strlen(string);
239 int k = *i;
240 char_block *s = *t;
242 while (s) {
243 while ((k<s->used) && (j<l) && (s->buffer[k] == string[j])) {
244 j++;
245 k++;
247 if (j == l) {
248 *i = k;
249 *t = s;
250 return( TRUE );
251 } else if ((k<s->used) && (s->buffer[k] != string[j])) {
252 return( FALSE );
254 s = s->next;
255 k = 0;
257 return( FALSE );
261 * write_file_troff - writes the buffer to stdout (troff).
262 * It prepends the number register set to 0.
265 void char_buffer::write_file_troff (void)
267 char_block *t=head;
268 int r;
270 writeString(".nr html2enable 0\n");
271 writeString(".nr htmlflip 0\n");
272 if (t != 0) {
273 do {
274 writeNbytes(t->buffer, t->used);
275 t = t->next;
276 } while ((t != head) && (t != 0));
278 if (close(stdoutfd) < 0)
279 sys_fatal("close");
281 // now we grab fd=1 so that the next pipe cannot use fd=1
282 if (stdoutfd == 1) {
283 if (dup(2) != stdoutfd) {
284 sys_fatal("dup failed to use fd=1");
290 * the image class remembers the position of all images in the postscript file
291 * and assigns names for each image.
294 struct imageItem {
295 imageItem *next;
296 int X1;
297 int Y1;
298 int X2;
299 int Y2;
300 char *imageName;
301 int resolution;
302 int pageNo;
304 imageItem (int x1, int y1, int x2, int y2, int page, int res, char *name);
305 ~imageItem ();
309 * imageItem - constructor
312 imageItem::imageItem (int x1, int y1, int x2, int y2, int page, int res, char *name)
314 X1 = x1;
315 Y1 = y1;
316 X2 = x2;
317 Y2 = y2;
318 pageNo = page;
319 resolution = res;
320 imageName = name;
321 next = 0;
325 * imageItem - deconstructor
328 imageItem::~imageItem ()
333 * imageList - class containing a list of imageItems.
336 class imageList {
337 private:
338 imageItem *head;
339 imageItem *tail;
340 int count;
341 public:
342 imageList();
343 ~imageList();
344 void add(int x1, int y1, int x2, int y2, int page, int res);
345 char *get(int i);
349 * imageList - constructor.
352 imageList::imageList ()
353 : head(0), tail(0), count(0)
358 * imageList - deconstructor.
361 imageList::~imageList ()
363 while (head != 0) {
364 imageItem *i = head;
365 head = head->next;
366 delete i;
371 * createImage - generates a png file from the information held in, i, and
372 * the postscript file.
375 static void createImage (imageItem *i)
377 if (i->X1 != -1) {
378 char buffer[4096];
380 sprintf(buffer,
381 "echo showpage | gs -q -dFirstPage=%d -dLastPage=%d -dSAFER -sDEVICE=%s -r%d -sOutputFile=- %s - 2> /dev/null | pnmcut %d %d %d %d | pnmtopng %s > %s.png \n",
382 i->pageNo, i->pageNo,
383 image_device,
384 image_res,
385 psFileName,
386 i->X1*image_res/POSTSCRIPTRES-IMAGE_BOARDER_PIXELS,
387 i->Y1*image_res/POSTSCRIPTRES-IMAGE_BOARDER_PIXELS,
388 (i->X2-i->X1)*image_res/POSTSCRIPTRES+2*IMAGE_BOARDER_PIXELS,
389 (i->Y2-i->Y1)*image_res/POSTSCRIPTRES+2*IMAGE_BOARDER_PIXELS,
390 TRANSPARENT,
391 i->imageName);
392 // fprintf(stderr, buffer);
393 system(buffer);
394 } else {
395 fprintf(stderr, "ignoring image as x1 coord is -1\n");
396 fflush(stderr);
401 * add - an image description to the imageList.
404 void imageList::add (int x1, int y1, int x2, int y2, int page, int res)
406 char *name = (char *)malloc(50);
408 if (name == 0)
409 sys_fatal("malloc");
411 if (x1 == -1) {
412 name[0] = (char)0;
413 } else {
414 count++;
415 sprintf(name, "grohtml-%d", count);
417 imageItem *i = new imageItem(x1, y1, x2, y2, page, res, name);
419 if (head == 0) {
420 head = i;
421 tail = i;
422 } else {
423 tail->next = i;
424 tail = i;
426 createImage(i);
430 * get - returns the name for image number, i.
433 char *imageList::get(int i)
435 imageItem *t=head;
437 while (i>0) {
438 if (i == 1) {
439 if (t->X1 == -1) {
440 return( NULL );
441 } else {
442 return( t->imageName );
445 t = t->next;
446 i--;
450 static imageList listOfImages; // list of images defined by the region file.
453 * write_file_html - writes the buffer to stdout (troff).
454 * It prepends the number register set to 1 and writes
455 * out the file replacing template image names with
456 * actual image names.
459 void char_buffer::write_file_html (void)
461 char_block *t =head;
462 int imageNo=0;
463 char *name;
464 int i=0;
466 writeString(".nr html2enable 1\n");
467 writeString(".nr htmlflip 1\n");
468 if (t != 0) {
469 stop();
470 do {
471 if (can_see(&t, &i, ".if '\\*(.T'html2' .IMAGE <pre-html-image>\n")) {
472 imageNo++;
473 name = listOfImages.get(imageNo);
474 if (name != 0) {
475 writeString(".if '\\*(.T'html2' .IMAGE \"");
476 writeString(name);
477 writeString(".png\"\n");
479 } else {
480 write_upto_newline(&t, &i);
482 } while (t != 0);
484 if (close(stdoutfd) < 0)
485 sys_fatal("close");
487 // now we grab fd=1 so that the next pipe cannot use fd=1
488 if (stdoutfd == 1) {
489 if (dup(2) != stdoutfd) {
490 sys_fatal("dup failed to use fd=1");
496 * generateImages - parses the region file and generates images
497 * from the postscript file. The region file
498 * contains the x1,y1 x2,y2 extents of each
499 * image.
502 static void generateImages (char *regionFileName)
504 pushBackBuffer *f=new pushBackBuffer(regionFileName);
505 char ch;
507 if (f->putPB('\n') == '\n') {
509 while (f->putPB(f->getPB()) != eof) {
510 if (f->isString("\ngrohtml-info:page")) {
511 int page= f->readInt();
512 int x1 = f->readInt();
513 int y1 = f->readInt();
514 int x2 = f->readInt();
515 int y2 = f->readInt();
516 int res = POSTSCRIPTRES; // --fixme-- prefer (f->readInt()) providing that troff can discover the value
517 listOfImages.add(x1, y1, x2, y2, page, res);
519 ch = f->getPB();
524 * replaceFd - replace a file descriptor, was, with, willbe.
527 static void replaceFd (int was, int willbe)
529 int dupres;
531 if (was != willbe) {
532 if (close(was)<0) {
533 sys_fatal("close");
535 dupres = dup(willbe);
536 if (dupres != was) {
537 sys_fatal("dup");
538 fprintf(stderr, "trying to replace fd=%d with %d dup used %d\n", was, willbe, dupres);
539 if (willbe == 1) {
540 fprintf(stderr, "likely that stdout should be opened before %d\n", was);
542 exit(1);
544 if (close(willbe) < 0) {
545 sys_fatal("close");
551 * waitForChild - waits for child, pid, to exit.
554 static void waitForChild (PID_T pid)
556 PID_T waitpd;
557 int status;
559 waitpd = wait(&status);
560 if (waitpd != pid)
561 sys_fatal("wait");
565 * alterDeviceTo - if toImage is set then the arg list is altered to include
566 * IMAGEDEVICE and we invoke groff rather than troff.
567 * else
568 * set -Thtml2 and troff
571 static void alterDeviceTo (int argc, char *argv[], int toImage)
573 int i=0;
575 if (toImage) {
576 while (i < argc) {
577 if (strcmp(argv[i], "-Thtml2") == 0) {
578 argv[i] = IMAGEDEVICE;
580 i++;
582 argv[1] = "groff"; /* rather than troff */
583 } else {
584 while (i < argc) {
585 if (strcmp(argv[i], IMAGEDEVICE) == 0) {
586 argv[i] = "-Thtml2";
588 i++;
590 argv[1] = "troff"; /* use troff */
595 * do_html - sets the troff number htmlflip and
596 * writes out the buffer to troff -Thtml
599 int char_buffer::do_html(int argc, char *argv[])
601 int pdes[2];
602 PID_T pid;
604 if (pipe(pdes) < 0)
605 sys_fatal("pipe");
607 alterDeviceTo(argc, argv, 0);
608 argv++; // skip pre-grohtml argv[0]
609 pid = fork();
610 if (pid < 0)
611 sys_fatal("fork");
613 if (pid == 0) {
614 // child
615 replaceFd(0, pdes[0]);
616 // close end we are not using
617 if (close(pdes[1])<0)
618 sys_fatal("close");
620 execvp(argv[0], argv);
621 error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0);
622 fflush(stderr); /* just in case error() doesn't */
623 exit(1);
624 } else {
625 // parent
627 replaceFd(1, pdes[1]);
628 // close end we are not using
629 if (close(pdes[0])<0)
630 sys_fatal("close");
632 write_file_html();
633 waitForChild(pid);
635 return( 0 );
639 * do_image - sets the troff number htmlflip and
640 * writes out the buffer to troff -Tps
643 int char_buffer::do_image(int argc, char *argv[])
645 PID_T pid;
646 int pdes[2];
648 if (pipe(pdes) < 0)
649 sys_fatal("pipe");
651 alterDeviceTo(argc, argv, 1);
652 argv++; // skip pre-grohtml argv[0]
654 pid = fork();
655 if (pid == 0) {
656 // child
658 #if defined(DEBUGGING)
659 int psFd = creat(psFileName, S_IWUSR|S_IRUSR);
660 int regionFd = creat(regionFileName, S_IWUSR|S_IRUSR);
661 #else
662 int psFd = mkstemp(psFileName);
663 int regionFd = mkstemp(regionFileName);
664 #endif
666 replaceFd(1, psFd);
667 replaceFd(0, pdes[0]);
668 replaceFd(2, regionFd);
670 // close end we are not using
671 if (close(pdes[1])<0)
672 sys_fatal("close");
674 execvp(argv[0], argv);
675 error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0);
676 fflush(stderr); /* just in case error() doesn't */
677 exit(1);
678 } else {
679 // parent
681 replaceFd(1, pdes[1]);
682 write_file_troff();
683 waitForChild(pid);
685 return( 0 );
688 static char_buffer inputFile;
692 * usage - emit usage arguments and exit.
695 void usage()
697 fprintf(stderr, "usage: %s troffname [ troff flags ] [ files ]\n", program_name);
698 exit(1);
702 * makeTempFiles - name the temporary files
705 static void makeTempFiles (void)
707 #if defined(DEBUGGING)
708 psFileName = "/tmp/prehtml-ps";
709 regionFileName = "/tmp/prehtml-region";
710 #else
711 psFileName = xtmptemplate("-ps-");
712 regionFileName = xtmptemplate("-regions-");
713 #endif
716 int main(int argc, char **argv)
718 program_name = argv[0];
719 int i; // skip over troff name
720 int found=0;
721 int ok=1;
723 for (i = 2; i < argc; i++) {
724 if (argv[i][0] == '-') {
725 if (argv[i][1] == 'v') {
726 extern const char *Version_string;
727 printf("GNU pre-grohtml (groff) version %s\n", Version_string);
728 exit(0);
730 } else {
731 ok = do_file(argv[i]);
732 if (! ok) {
733 return( 0 );
735 found = 1;
739 if (! found) {
740 do_file("-");
742 makeTempFiles();
743 ok = inputFile.do_image(argc, argv);
744 if (ok == 0) {
745 generateImages(regionFileName);
746 ok = inputFile.do_html(argc, argv);
748 return ok;
751 static int do_file(const char *filename)
753 FILE *fp;
755 current_filename = filename;
756 if (strcmp(filename, "-") == 0) {
757 fp = stdin;
758 } else {
759 fp = fopen(filename, "r");
760 if (fp == 0) {
761 error("can't open `%1': %2", filename, strerror(errno));
762 return 0;
766 if (inputFile.read_file(fp)) {
769 if (fp != stdin)
770 fclose(fp);
771 current_filename = 0;
772 return 1;