1 /*  eqn2img: converts LaTeX equation to image, part of gladtex
2     Project homepage at http://gladtex.sourceforge.net
3     Copyright (C) 1999-2010 Martin G. Gulbrandsen
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 /* Uncomment this for gif support */
21 /*#define GIF*/
22 
23 #define BACKGROUND 0xFF
24 
25 #define MAX(a,b) ((a)>(b) ? (a) : (b))
26 
27 /* returns least integer >= a/b */
28 #define DIV(a,b) (((a)/(b))*(b) == (a) ? (a)/(b) : (a)/(b) + 1)
29 
30 #define USAGE "\
31 This utility is part of\n\
32 gladtex version 1.2, Copyright (C) 1999-2010 Martin G. Gulbrandsen\n\
33 \n\
34 gladtex comes with ABSOLUTELY NO WARRANTY. This is free software,\n\
35 and you are welcome to redistribute it under certain conditions;\n\
36 see the file COPYING for details.\n\
37 \n\
38 Usage: eqn2img [OPTION]...\n\
39 \n\
40 -o name     output filename\n\
41 -v          print verbose information\n\
42 -f format   store images in 'format' (png by default)\n\
43 -s n        set oversampling factor for antialiasing to 'n' (4 by default)\n\
44 -r dpi      set output resolution to 'dpi' dots per inch\n\
45 -p string   add 'string' to LaTeX preamble (e.g. \\usepackage{...})\n\
46 -e env      embed equation in \\begin{env}..\\end{env} (e.g. -e 'align*')\n\
47 -c colour   set foreground RGB colour (000000 by default)\n\
48 -b colour   set background RGB colour (A0A0A0 by default, ignored for png)\n\
49 -t          turn transparency OFF\n\
50 \n\
51 LaTeX maths string is read from stdin. Image written to stdout unless filename\n\
52 specified wit -o option. Run with -f ? to list supported image formats.\n"
53 
54 #include <png.h>
55 #include <string.h>
56 #include <stdio.h>
57 #include <unistd.h>
58 #include <assert.h>
59 #include <stdlib.h>
60 #ifdef GIF
61 #include <gif_lib.h>
62 #endif
63 
64 #define NEW(type,size) (type *) malloc(sizeof(type)*(size))
65 
66 enum img_format {png
67 #ifdef GIF
68 ,gif
69 #endif
70 };
71 
72 struct rgb {
73   unsigned int red;
74   unsigned int green;
75   unsigned int blue;
76 };
77 
78 void split_name(char *filename, char **dir, char **base, char **ext);
79 char *mk_basename(char *img_name);
80 char *mk_dirname(char *img_name);
81 png_structp png_initialize(FILE *fp, int *width, int *height);
82 png_bytepp png_read(char *filename, int *width, int *height);
83 png_bytepp img_modify(png_bytepp in_img, int width, int height, int supersample,
84 		      int *new_width, int *new_height, int *baseline);
85 int png_write(png_bytepp image, char *basename, int width, int height,
86 	      struct rgb foreground, struct rgb background, int transparency);
87 #ifdef GIF
88 int gif_write(png_bytepp image, char *basename, int width, int height,
89 	      struct rgb foreground, struct rgb background, int transparency);
90 #endif
91 int to_dvi(char *base_name, char *preamble, char *env, int verbose);
92 int to_ps(char *base_name, int verbose);
93 int to_png(char *base_name, int dpi, int supersample, int verbose);
94 
main(int argc,char ** argv)95 int main(int argc, char **argv)
96 {
97   char c;
98   enum img_format format = png;
99   int supersample = 4;
100   char *img_name = NULL;
101   char *basename = NULL;
102   char *dirname = NULL;
103   char *extname = NULL;
104   char *png_inname = NULL;
105   char *env = NULL;
106   png_bytepp in_image, out_image;
107   int height, width;
108   int new_height, new_width, baseline;
109   int retval=0;
110   int verbose=0;
111   int transparency=1;
112   int dpi=100;
113   struct rgb foreground = {0,0,0};
114   struct rgb background = {0xA0,0xA0,0xA0};
115   char *preamble = NULL;
116   int i;
117 
118   /* parse command-line options */
119 
120   while((c = getopt(argc, argv, "f:s:o:e:tvc:b:r:p:")) != EOF) {
121     switch(c) {
122     case 'f':
123       if(!strcmp("png", optarg)) {
124 	format = png;
125       }
126 #ifdef GIF
127       else if(!strcmp("gif", optarg)) {
128 	format = gif;
129       }
130 #endif
131       else if(optarg[0]=='?') {
132 	printf("Supported image formats: png%s",
133 #ifdef GIF
134 	       ", gif"
135 #endif
136 	       "\n");
137 	return 0;
138       }
139       else {
140 	fprintf(stderr, "\nUnknown format '%s'\n", optarg);
141 	return 1;
142       }
143       break;
144     case 's':
145       supersample = atoi(optarg);
146       if(supersample <= 0) {
147 	fprintf(stderr, "\nOversampling factor %s is not a positive integer\n", optarg);
148 	return 1;
149       }
150       break;
151     case 'o':
152       img_name = NEW(char, strlen(optarg)+1);
153       assert(img_name);
154       strcpy(img_name, optarg);
155       break;
156     case 'e':
157       env = NEW(char, strlen(optarg)+1);
158       assert(env);
159       strcpy(env, optarg);
160       break;
161     case 'v':
162       verbose = 1;
163       break;
164     case 't':
165       transparency = 0;
166       break;
167     case 'c':
168       sscanf(optarg, "%02x%02x%02x", &foreground.red, &foreground.green, &foreground.blue);
169       break;
170     case 'b':
171       sscanf(optarg, "%02x%02x%02x", &background.red, &background.green, &background.blue);
172       break;
173     case 'p':
174       preamble = NEW(char, strlen(optarg)+1);
175       assert(preamble);
176       strcpy(preamble, optarg);
177       break;
178     case 'r':
179       dpi = atoi(optarg);
180       break;
181     default:
182       printf(USAGE);
183       return 0;
184     }
185   }
186 
187   if(argc != optind) {
188     printf(USAGE);
189     return 0;
190   }
191 
192   if(!preamble) {
193     preamble = NEW(char, 1);
194     assert(preamble);
195     preamble[0] = '\0';
196   }
197 
198   if(!env) {
199     env = NEW(char, 12);
200     assert(env);
201     strcpy(env, "displaymath");
202   }
203 
204   if(img_name) {
205     split_name(img_name, &dirname, &basename, &extname);
206     if(extname)
207       sprintf(img_name, "%s.%s", basename, extname);
208     else
209       img_name = basename;
210   }
211   else {
212     basename = NEW(char, 7);
213     assert(basename);
214     strcpy(basename, "noname");
215     dirname = NEW(char, 2);
216     assert(dirname);
217     strcpy(dirname, ".");
218   }
219 
220   retval = chdir(dirname);
221   assert(!retval);
222 
223   /* convert LaTeX equation text to image */
224   if(to_dvi(basename, preamble, env, verbose) ||
225      to_ps(basename, verbose) ||
226      to_png(basename, dpi, supersample, verbose)) {
227     return 1;
228   }
229 
230   /* read input png file */
231   png_inname = NEW(char, strlen(basename)+8);
232   assert(png_inname);
233   sprintf(png_inname, "%s.ps.png", basename);
234   in_image = png_read(png_inname, &width, &height);
235   unlink(png_inname);
236   if(!in_image) {
237     fprintf(stderr, "\nError reading '%s'\n", png_inname);
238     return 1;
239   }
240 
241   /* clean up */
242 
243   free(preamble);
244   free(dirname);
245   free(basename);
246   free(env);
247   free(png_inname);
248 
249   /* process image */
250 
251   out_image = img_modify(in_image, width, height, supersample, &new_width, &new_height, &baseline);
252 
253   for(i=0; i<height; i++)
254     free(in_image[i]);
255   free(in_image);
256 
257   /* write output file */
258 
259   switch(format) {
260   case png:
261     if(verbose)
262       fprintf(stderr, " -> Writing png image");
263     retval = png_write(out_image, img_name, new_width, new_height, foreground, background, transparency);
264     break;
265 #ifdef GIF
266   case gif:
267     if(verbose)
268       fprintf(stderr, " -> Writing gif image");
269     retval = gif_write(out_image, img_name, new_width, new_height, foreground, background, transparency);
270     break;
271 #endif
272   }
273 
274   /* clean up */
275 
276   for(i=0; i<new_height; i++)
277     free(out_image[i]);
278   free(out_image);
279 
280   if(retval) {
281     fprintf(stderr, "\nError writing image to '%s'\n", img_name ? img_name : "stdout");
282     return 1;
283   }
284 
285   if(img_name)
286     printf("WIDTH=%i HEIGHT=%i STYLE=\"vertical-align: -%ipx; margin: 0;\"", new_width, new_height, baseline);
287   else /* image being written to stdout, use stderr instead */
288     fprintf(stderr, "WIDTH=%i HEIGHT=%i STYLE=\"vertical-align: -%ipx; margin: 0;\"", new_width, new_height, baseline);
289 
290   return 0;
291 }
292 
293 /*
294   Decomposes filename = "dir/base.ext" into its components "dir",
295   "base" and "ext". If "dir" is missing (no "/" in filename), "." is
296   returned. If "ext" is missing, NULL is returned, this must be taken
297   care of by the caller. To clarify:
298     "dir/base.ext" -> "dir" + "base" + "ext"
299     "/base.ext"    -> "" + "base" + "ext"
300     "base.ext"     -> "." + "base" + "ext"
301     "dir/base"     -> "dir" + "base" + NULL
302     "dir/base."    -> "dir" + "base + ""
303   Memory for these strings is allocated by this function.
304  */
305 
split_name(char * filename,char ** dir,char ** base,char ** ext)306 void split_name(char *filename, char **dir, char **base, char **ext)
307 {
308   char *dot, *slash;
309   slash = strrchr(filename, '/');
310 
311   if(slash==NULL) {
312     *dir = NEW(char,2);
313     assert(*dir);
314     strcpy(*dir, ".");
315   }
316   else {
317     *dir = NEW(char, slash-filename+1);
318     assert(*dir);
319     strncpy(*dir, filename, slash-filename);
320     (*dir)[slash-filename] = '\0';
321     filename = slash+1; /* advance pointer to after slash */
322   }
323 
324   dot = strrchr(filename, '.');
325   if(dot==NULL) {
326     *base = NEW(char, strlen(filename)+1);
327     assert(*base);
328     strcpy(*base, filename);
329     *ext = NULL;
330   }
331   else {
332     *base = NEW(char, dot-filename+1);
333     assert(*base);
334     strncpy(*base, filename, dot-filename);
335     (*base)[dot-filename] = '\0';
336     filename = dot+1;
337     *ext = NEW(char, strlen(filename)+1);
338     assert(*ext);
339     strncpy(*ext, filename, strlen(filename));
340     (*ext)[strlen(filename)] = '\0';
341   }
342 }
343 
344 /*
345   input:
346     filename
347   output:
348     width, height
349   returns read png image, or NULL on error
350 */
png_read(char * filename,int * width,int * height)351 png_bytepp png_read(char *filename, int *width, int *height)
352 {
353   png_byte bit_depth, color_type;
354   png_bytepp image;
355   png_structp png_ptr;
356   png_infop info_ptr;
357   int row;
358   FILE *fp;
359 
360   fp = fopen(filename, "rb");
361   if(!fp)
362     return NULL;
363 
364   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
365   assert(png_ptr);
366 
367   info_ptr = png_create_info_struct(png_ptr);
368   assert(info_ptr);
369 
370   if(setjmp(png_jmpbuf(png_ptr))) {
371     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
372     fclose(fp);
373     return NULL;
374   }
375 
376   png_init_io(png_ptr, fp);
377 
378   png_read_info(png_ptr, info_ptr);
379 
380   /* change pixel encoding to 8 bits grayscale */
381   color_type = png_get_color_type(png_ptr, info_ptr);
382   bit_depth = png_get_bit_depth(png_ptr, info_ptr);
383 
384   if(bit_depth < 8)
385     png_set_packing(png_ptr);
386   if(bit_depth == 16)
387     png_set_strip_16(png_ptr);
388   if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
389     png_set_expand(png_ptr);
390   if(color_type != PNG_COLOR_TYPE_GRAY) {
391     fprintf(stderr, "\nInput file is not grayscale.\n");
392     return NULL;
393   }
394 
395   png_read_update_info(png_ptr, info_ptr);
396 
397   *width = png_get_image_width(png_ptr, info_ptr);
398   *height = png_get_image_height(png_ptr, info_ptr);
399 
400   image = NEW(png_bytep, *height);
401   assert(image);
402   for(row=0; row<*height; row++) {
403     image[row] = NEW(png_byte, *width);
404     assert(image[row]);
405   }
406 
407   png_read_image(png_ptr, image);
408   png_read_end(png_ptr, NULL);
409 
410   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
411   fclose(fp);
412 
413   return image;
414 }
415 
416 /*
417  get_pixel
418 
419  Returns the pixel color at coordinates (row, col). Negative
420  coordinates and coordinates larger than width/height are
421  allowed, and will return bg_color.
422 */
423 
get_pixel(png_bytepp img,int col,int row,int width,int height,png_byte bg_color)424 png_byte get_pixel(png_bytepp img, int col, int row, int width, int height, png_byte bg_color)
425 {
426   if(row<0 || col<0 || row>=height || col>=width)
427     return bg_color;
428 
429   return img[row][col];
430 }
431 
432 /*
433    img_modify does the following:
434    - find 'leading dot' in in_img (i.e. some non-white area in the leftmost end:
435      the perl script prepends a . (full stop) to the equation being processed)
436    - copy everything to the right of the dot to out_img (returned value), while
437      downscaling+antialiasing the image (simply by averaging a 'supersample' times
438      'supersample' block to a single pixel)
439    - return (in "baseline") the number of pixels from the bottom of the (downscaled)
440      image to the baseline of the equation
441 
442    input:
443      in_img, widt, height, supersample
444    output:
445      new_width, new_height, baseline
446    returns new png image
447 */
448 
img_modify(png_bytepp in_img,int width,int height,int supersample,int * new_width,int * new_height,int * baseline)449 png_bytepp img_modify(png_bytepp in_img, int width, int height, int supersample,
450 		      int *new_width, int *new_height, int *baseline)
451 {
452   int row, col;
453   int dot_found = 0;
454   int edge_found = 0;
455   int row_count = 0;
456   long row_sum = 0;
457   int row_center;
458   int col_start = 0;
459   png_bytepp out_img;
460 
461   assert(in_img);
462 
463   /* find dot: vertical center and horisontal end
464      (maybe it's better to find vertical end instead of center?)
465      also wipe out dot */
466   for(col=0; col < width; col++) {
467     int empty_col = 1;
468     for(row=0; row < height; row++) {
469       if((in_img[row])[col] != BACKGROUND) {
470 	(in_img[row])[col] = BACKGROUND;
471 	dot_found = 1;
472 	empty_col = 0;
473 	row_count++;
474 	row_sum += row;
475       }
476     }
477     if(dot_found && empty_col) {
478       /* col is 1st blank column after dot */
479       col_start = col;
480       break;
481     }
482   }
483 
484   if(col == width) {
485     fprintf(stderr, "\nImage doesn't start with a dot\n");
486     exit(1);
487   }
488 
489   /* the next step is a workaround: sometimes dvips produces a wrong
490      boundingbox in the ps-file (I've seen this when using the \mathbb
491      font), resulting in a too wide image (the height is correct but
492      the box is extended to the right). to solve this we remove any
493      blank area to the right */
494   edge_found=0;
495   for(col=width-1; col>=0 && !edge_found; col--) {
496     for(row=0; row < height; row++) {
497       if((in_img[row])[col] != BACKGROUND) {
498 	width = col+supersample; /* this is the true width (we add a small border) */
499 	edge_found = 1;
500 	break;
501       }
502     }
503   }
504 
505   /* row_sum/row_count should be the centre of the "leading dot", but
506      the dot itself lies above the baseline, so we add a few pixels */
507   row_center = row_sum/row_count+supersample*2;
508   *new_height = DIV(height, supersample);
509   *new_width = DIV(width-col_start, supersample);
510   *baseline = DIV(height-row_center,supersample);
511 
512   /* allocate memory for new image and initialize to all background */
513   out_img = NEW(png_bytep, *new_height);
514   assert(out_img);
515   for(row=0; row < *new_height; row++) {
516     out_img[row] = NEW(png_byte, *new_width);
517     assert(out_img[row]);
518     memset(out_img[row], BACKGROUND, *new_width);
519   }
520 
521   /* I guess the following could be done more efficiently, but this doesn't
522      seem to be the bottle neck anyway (disc i/o is) */
523   for(row=0; row < *new_height; row++) {
524     for(col=0; col < *new_width; col++) {
525       int delta, epsilon;
526       long pixel_sum = 0;
527       for(delta=0; delta < supersample; delta++) {
528 	for(epsilon=0; epsilon < supersample; epsilon++) {
529 	  pixel_sum += get_pixel(in_img, col_start + supersample*col + epsilon,
530 				 supersample*row + delta,
531 				 width, height, BACKGROUND);
532 	}
533       }
534       out_img[row][col] = pixel_sum/(supersample*supersample);
535     }
536   }
537 
538   return out_img;
539 }
540 
541 /*  write image to png file */
542 
png_write(png_bytepp image,char * img_name,int width,int height,struct rgb foreground,struct rgb background,int transparency)543 int png_write(png_bytepp image, char *img_name, int width, int height,
544 	      struct rgb foreground, struct rgb background, int transparency)
545 {
546   FILE *fp=NULL;
547   int i;
548   png_byte trans[256];
549   png_structp png_ptr;
550   png_color pal[256];
551   png_infop info_ptr;
552 
553   for(i=0; i<256; i++) {
554     if(transparency) {
555       pal[i].red = foreground.red;
556       pal[i].green = foreground.green;
557       pal[i].blue = foreground.blue;
558       trans[i] = 255-i;
559     }
560     else {
561       pal[i].red = (i*background.red + (255-i)*foreground.red)/255;
562       pal[i].green = (i*background.green + (255-i)*foreground.green)/255;
563       pal[i].blue = (i*background.blue + (255-i)*foreground.blue)/255;
564       trans[i] = 255;
565     }
566   }
567 
568   if(img_name) {
569     fp = fopen(img_name, "wb");
570     if(!fp)
571       return -1;
572   }
573   else
574     fp = stdout;
575 
576   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
577   assert(png_ptr);
578 
579   info_ptr = png_create_info_struct(png_ptr);
580   assert(info_ptr);
581 
582   /* error handling, libpng longjmps here on any error */
583   if(setjmp(png_jmpbuf(png_ptr))) {
584     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
585     fclose(fp);
586     return -1;
587   }
588 
589   png_init_io(png_ptr, fp);
590 
591   png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_PALETTE,
592 	       PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
593   png_set_PLTE(png_ptr, info_ptr, pal, 256);
594   png_set_tRNS(png_ptr, info_ptr, trans, 256, NULL);
595 
596   png_write_info(png_ptr, info_ptr);
597   png_write_image(png_ptr, image);
598   png_write_end(png_ptr, NULL);
599 
600   png_destroy_write_struct(&png_ptr, &info_ptr);
601 
602   return 0;
603 }
604 
605 
606 /* write image to gif file */
607 
608 #ifdef GIF
gif_write(png_bytepp image,char * img_name,int width,int height,struct rgb foreground,struct rgb background,int transparency)609 int gif_write(png_bytepp image, char *img_name, int width, int height,
610 	      struct rgb foreground, struct rgb background, int transparency)
611 
612 {
613   GifFileType *fp;
614   GifColorType pal[256];
615   int i, row;
616   ColorMapObject *color_map;
617   unsigned char gc_ext[5] = { /* graphic control extension (to get transparent background) */
618     0x01, /* flags: transparency color flag on */
619     0x00, /* delay time */
620     0x00, /* delay time cntd */
621     BACKGROUND, /* transparent color index */
622     0x00  /* block terminator (probably not needed?) */
623   };
624 
625   if(img_name) {
626 #if GIFLIB_MAJOR >= 5
627     fp = EGifOpenFileName(img_name, 0, NULL);
628 #else
629     fp = EGifOpenFileName(img_name, 0);
630 #endif
631     if(!fp)
632       return -1;
633   }
634   else
635 #if GIFLIB_MAJOR >= 5
636     fp = EGifOpenFileHandle(STDOUT_FILENO, NULL);
637 #else
638     fp = EGifOpenFileHandle(STDOUT_FILENO);
639 #endif
640 
641   for(i=0; i<256; i++) {
642     pal[i].Red = (i*background.red + (255-i)*foreground.red)/255;
643     pal[i].Green = (i*background.green + (255-i)*foreground.green)/255;
644     pal[i].Blue = (i*background.blue + (255-i)*foreground.blue)/255;
645   }
646 #if GIFLIB_MAJOR >= 5
647   color_map = GifMakeMapObject(256, pal);
648 #else
649   color_map = MakeMapObject(256, pal);
650 #endif
651 
652   /* EGifSetGifVersion("89a"); this causes segfault (but is really required for transparency, I think) */
653   EGifPutScreenDesc(fp, width, height, 256, 255, color_map);
654   if(transparency) {
655     EGifPutExtension(fp, 0xF9, 4, gc_ext); /* this sets transparent color to background */
656   }
657   EGifPutImageDesc(fp, 0, 0, width, height, 0, color_map);
658 
659   for(row=0; row<height; row++) {
660     if(EGifPutLine(fp, image[row], width) != GIF_OK)
661       return -1;
662   }
663 
664 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1 || GIFLIB_MAJOR > 5
665   EGifCloseFile(fp, NULL);
666 #else
667   EGifCloseFile(fp);
668 #endif
669 
670   return 0;
671 }
672 #endif
673 
to_dvi(char * basename,char * preamble,char * env,int verbose)674 int to_dvi(char *basename, char *preamble, char *env, int verbose) {
675   char *tex_name;
676   char *cmd;
677   FILE *tex_file;
678   int retval;
679 
680   tex_name = NEW(char, strlen(basename)+5);
681   assert(tex_name);
682   sprintf(tex_name, "%s.tex", basename);
683 
684   if(verbose)
685     fprintf(stderr, "tex -> dvi");
686 
687   tex_file = fopen(tex_name, "w");
688   fprintf(tex_file, "\\nonstopmode\n"
689 	         "\\documentclass[12pt]{article}\n"
690 	         "\\pagestyle{empty}\n"
691                  "%s\n"
692                  "\\begin{document}\n"
693                  "\\begin{%s}\n"
694                  ".",
695 	         preamble, env);
696   /* copy equation text from stdin to tex_file */
697   while(!feof(stdin)) {
698     char buf[256];
699     int n;
700     n = fread(buf, 1, 256, stdin);
701     fwrite(buf, 1, n, tex_file);
702   }
703   fprintf(tex_file, "\\end{%s}\n\\end{document}\n", env);
704   fclose(tex_file);
705 
706   cmd = NEW(char, strlen(tex_name) + 19);
707   assert(cmd);
708   sprintf(cmd, "latex %s > /dev/null", tex_name);
709   retval = system(cmd);
710   unlink(tex_name);
711   sprintf(tex_name, "%s.aux", basename);
712   unlink(tex_name);
713   if(retval) {
714     sprintf(tex_name, "%s.dvi", basename);
715     unlink(tex_name);
716     fprintf(stderr, "\nError running LaTeX\n");
717     return -1;
718   }
719   sprintf(tex_name, "%s.log", basename);
720   unlink(tex_name);
721 
722   free(tex_name);
723   free(cmd);
724   return 0;
725 }
726 
to_ps(char * basename,int verbose)727 int to_ps(char *basename, int verbose) {
728   char *cmd;
729 
730   if(verbose)
731     fprintf(stderr, " -> ps");
732 
733   cmd = NEW(char, 2*strlen(basename) + 46);
734   sprintf(cmd, "dvips -q -E -o %s.ps %s.dvi > /dev/null 2> /dev/null", basename, basename);
735   if(system(cmd)) {
736     fprintf(stderr, "\nError running dvips\n");
737     return -1;
738   }
739   sprintf(cmd, "%s.dvi", basename);
740   unlink(cmd);
741   free(cmd);
742   return 0;
743 }
744 
to_png(char * basename,int dpi,int supersample,int verbose)745 int to_png(char *basename, int dpi, int supersample, int verbose) {
746   char *cmd;
747   int start_x, start_y, end_x, end_y;
748   int xsize, ysize, xoffset, yoffset;
749   int retval;
750   FILE *grep, *gs;
751 
752   if(verbose)
753     fprintf(stderr, " -> png");
754 
755   cmd = NEW(char, strlen(basename)+25);
756   sprintf(cmd, "grep \"%%BoundingBox\" %s.ps", basename);
757   grep = popen(cmd, "r");
758   retval = fscanf(grep, "%%%%BoundingBox: %i %i %i %i", &start_x, &start_y, &end_x, &end_y);
759   assert(retval != EOF);
760   pclose(grep);
761   free(cmd);
762 
763   xsize = 1 + ((end_x - start_x) * dpi)/72;
764   ysize = 1 + ((end_y - start_y) * dpi)/72;
765   xsize *= supersample;
766   ysize *= supersample;
767   xoffset = start_x;
768   yoffset = start_y;
769 
770   cmd = NEW(char, strlen(basename)+200); /* I didn't count those 200.. */
771   sprintf(cmd, "gs -sDEVICE=pngmono -sOutputFile=%s.ps.png -r%i -g%ix%i -q - %s.ps",
772 	  basename, dpi*supersample, xsize, ysize, basename);
773   gs = popen(cmd, "w");
774   fprintf(gs, "%i neg %i neg translate\n", xoffset, yoffset);
775   pclose(gs);
776   sprintf(cmd, "%s.ps", basename);
777   unlink(cmd);
778   free(cmd);
779 
780   return 0;
781 }
782