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