summaryrefslogtreecommitdiff
path: root/source/tools/mudraw.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/tools/mudraw.c')
-rw-r--r--source/tools/mudraw.c1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/source/tools/mudraw.c b/source/tools/mudraw.c
new file mode 100644
index 00000000..57bd4be2
--- /dev/null
+++ b/source/tools/mudraw.c
@@ -0,0 +1,1090 @@
+/*
+ * mudraw -- command line tool for drawing pdf/xps/cbz documents
+ */
+
+#include "mupdf/fitz.h"
+#include "mupdf/pdf.h" /* for mujstest */
+
+#ifdef _MSC_VER
+#include <winsock2.h>
+#define main main_utf8
+#else
+#include <sys/time.h>
+#endif
+
+enum { TEXT_PLAIN = 1, TEXT_HTML = 2, TEXT_XML = 3 };
+
+enum { OUT_PNG, OUT_PPM, OUT_PNM, OUT_PAM, OUT_PGM, OUT_PBM, OUT_SVG, OUT_PWG, OUT_PCL };
+
+enum { CS_INVALID, CS_UNSET, CS_MONO, CS_GRAY, CS_GRAYALPHA, CS_RGB, CS_RGBA };
+
+typedef struct
+{
+ char *suffix;
+ int format;
+} suffix_t;
+
+static const suffix_t suffix_table[] =
+{
+ { ".png", OUT_PNG },
+ { ".pgm", OUT_PGM },
+ { ".ppm", OUT_PPM },
+ { ".pnm", OUT_PNM },
+ { ".pam", OUT_PAM },
+ { ".pbm", OUT_PBM },
+ { ".svg", OUT_SVG },
+ { ".pwg", OUT_PWG },
+ { ".pcl", OUT_PCL }
+};
+
+typedef struct
+{
+ char *name;
+ int colorspace;
+} cs_name_t;
+
+static const cs_name_t cs_name_table[] =
+{
+ { "m", CS_MONO },
+ { "mono", CS_MONO },
+ { "g", CS_GRAY },
+ { "gray", CS_GRAY },
+ { "grey", CS_GRAY },
+ { "ga", CS_GRAYALPHA },
+ { "grayalpha", CS_GRAYALPHA },
+ { "greyalpha", CS_GRAYALPHA },
+ { "rgb", CS_RGB },
+ { "rgba", CS_RGBA },
+ { "rgbalpha", CS_RGBA }
+};
+
+typedef struct
+{
+ int format;
+ int default_cs;
+ int permitted_cs[6];
+} format_cs_table_t;
+
+static const format_cs_table_t format_cs_table[] =
+{
+ { OUT_PNG, CS_RGB, { CS_GRAY, CS_GRAYALPHA, CS_RGB, CS_RGBA } },
+ { OUT_PPM, CS_RGB, { CS_GRAY, CS_RGB } },
+ { OUT_PNM, CS_GRAY, { CS_GRAY, CS_RGB } },
+ { OUT_PAM, CS_RGBA, { CS_RGBA } },
+ { OUT_PGM, CS_GRAY, { CS_GRAY, CS_RGB } },
+ { OUT_PBM, CS_MONO, { CS_MONO } },
+ { OUT_SVG, CS_RGB, { CS_RGB } },
+ { OUT_PWG, CS_RGB, { CS_MONO, CS_GRAY, CS_RGB } },
+ { OUT_PCL, CS_MONO, { CS_MONO } }
+};
+
+/*
+ A useful bit of bash script to call this to generate mjs files:
+ for f in tests_private/pdf/forms/v1.3/ *.pdf ; do g=${f%.*} ; echo $g ; ../mupdf.git/win32/debug/mudraw.exe -j $g.mjs $g.pdf ; done
+
+ Remove the space from "/ *.pdf" before running - can't leave that
+ in here, as it causes a warning about a possibly malformed comment.
+*/
+
+static char lorem[] =
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum "
+"vehicula augue id est lobortis mollis. Aenean vestibulum metus sed est "
+"gravida non tempus lacus aliquet. Nulla vehicula lobortis tincidunt. "
+"Donec malesuada nisl et lacus condimentum nec tincidunt urna gravida. "
+"Sed dapibus magna eu velit ultrices non rhoncus risus lacinia. Fusce "
+"vitae nulla volutpat elit dictum ornare at eu libero. Maecenas felis "
+"enim, tempor a tincidunt id, commodo consequat lectus.\n"
+"Morbi tincidunt adipiscing lacus eu dignissim. Pellentesque augue elit, "
+"ultrices vitae fermentum et, faucibus et purus. Nam ante libero, lacinia "
+"id tincidunt at, ultricies a lorem. Donec non neque at purus condimentum "
+"eleifend quis sit amet libero. Sed semper, mi ut tempus tincidunt, lacus "
+"eros pellentesque lacus, id vehicula est diam eu quam. Integer tristique "
+"fringilla rhoncus. Phasellus convallis, justo ut mollis viverra, dui odio "
+"euismod ante, nec fringilla nisl mi ac diam.\n"
+"Maecenas mi urna, ornare commodo feugiat id, cursus in massa. Vivamus "
+"augue augue, aliquam at varius eu, venenatis fermentum felis. Sed varius "
+"turpis a felis ultrices quis aliquet nunc tincidunt. Suspendisse posuere "
+"commodo nunc non viverra. Praesent condimentum varius quam, vel "
+"consectetur odio volutpat in. Sed malesuada augue ut lectus commodo porta. "
+"Vivamus eget mauris sit amet diam ultrices sollicitudin. Cras pharetra leo "
+"non elit lacinia vulputate.\n"
+"Donec ac enim justo, ornare scelerisque diam. Ut vel ante at lorem "
+"placerat bibendum ultricies mattis metus. Phasellus in imperdiet odio. "
+"Proin semper lacinia libero, sed rutrum eros blandit non. Duis tincidunt "
+"ligula est, non pellentesque mauris. Aliquam in erat scelerisque lacus "
+"dictum suscipit eget semper magna. Nullam luctus imperdiet risus a "
+"semper.\n"
+"Curabitur sit amet tempor sapien. Quisque et tortor in lacus dictum "
+"pulvinar. Nunc at nisl ut velit vehicula hendrerit. Mauris elementum "
+"sollicitudin leo ac ullamcorper. Proin vel leo nec justo tempus aliquet "
+"nec ut mi. Pellentesque vel nisl id dui hendrerit fermentum nec quis "
+"tortor. Proin eu sem luctus est consequat euismod. Vestibulum ante ipsum "
+"primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce "
+"consectetur ultricies nisl ornare dictum. Cras sagittis consectetur lorem "
+"sed posuere. Mauris accumsan laoreet arcu, id molestie lorem faucibus eu. "
+"Vivamus commodo, neque nec imperdiet pretium, lorem metus viverra turpis, "
+"malesuada vulputate justo eros sit amet neque. Nunc quis justo elit, non "
+"rutrum mauris. Maecenas blandit condimentum nibh, nec vulputate orci "
+"pulvinar at. Proin sed arcu vel odio tempus lobortis sed posuere ipsum. Ut "
+"feugiat pellentesque tortor nec ornare.\n";
+
+static char *output = NULL;
+static float resolution = 72;
+static int res_specified = 0;
+static float rotation = 0;
+
+static int showxml = 0;
+static int showtext = 0;
+static int showtime = 0;
+static int showmd5 = 0;
+static int showoutline = 0;
+static int uselist = 1;
+static int alphabits = 8;
+static float gamma_value = 1;
+static int invert = 0;
+static int width = 0;
+static int height = 0;
+static int fit = 0;
+static int errored = 0;
+static int ignore_errors = 0;
+static int output_format;
+static int append = 0;
+static int out_cs = CS_UNSET;
+
+static fz_text_sheet *sheet = NULL;
+static fz_colorspace *colorspace;
+static char *filename;
+static int files = 0;
+fz_output *out = NULL;
+
+static char *mujstest_filename = NULL;
+static FILE *mujstest_file = NULL;
+static int mujstest_count = 0;
+
+static struct {
+ int count, total;
+ int min, max;
+ int minpage, maxpage;
+ char *minfilename;
+ char *maxfilename;
+} timing;
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "usage: mudraw [options] input [pages]\n"
+ "\t-o -\toutput filename (%%d for page number)\n"
+ "\t\tsupported formats: pgm, ppm, pam, png, pbm\n"
+ "\t-p -\tpassword\n"
+ "\t-r -\tresolution in dpi (default: 72)\n"
+ "\t-w -\twidth (in pixels) (maximum width if -r is specified)\n"
+ "\t-h -\theight (in pixels) (maximum height if -r is specified)\n"
+ "\t-f -\tfit width and/or height exactly (ignore aspect)\n"
+ "\t-c -\tcolorspace {mono,gray,grayalpha,rgb,rgba}\n"
+ "\t-b -\tnumber of bits of antialiasing (0 to 8)\n"
+ "\t-g\trender in grayscale\n"
+ "\t-m\tshow timing information\n"
+ "\t-t\tshow text (-tt for xml, -ttt for more verbose xml)\n"
+ "\t-x\tshow display list\n"
+ "\t-d\tdisable use of display list\n"
+ "\t-5\tshow md5 checksums\n"
+ "\t-R -\trotate clockwise by given number of degrees\n"
+ "\t-G -\tgamma correct output\n"
+ "\t-I\tinvert output\n"
+ "\t-l\tprint outline\n"
+ "\t-j -\tOutput mujstest file\n"
+ "\t-i\tignore errors and continue with the next file\n"
+ "\tpages\tcomma separated list of ranges\n");
+ exit(1);
+}
+
+static int gettime(void)
+{
+ static struct timeval first;
+ static int once = 1;
+ struct timeval now;
+ if (once)
+ {
+ gettimeofday(&first, NULL);
+ once = 0;
+ }
+ gettimeofday(&now, NULL);
+ return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000;
+}
+
+static int isrange(char *s)
+{
+ while (*s)
+ {
+ if ((*s < '0' || *s > '9') && *s != '-' && *s != ',')
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
+static void escape_string(FILE *out, int len, const char *string)
+{
+ while (len-- && *string)
+ {
+ char c = *string++;
+ switch (c)
+ {
+ case '\n':
+ fputc('\\', out);
+ fputc('n', out);
+ break;
+ case '\r':
+ fputc('\\', out);
+ fputc('r', out);
+ break;
+ case '\t':
+ fputc('\\', out);
+ fputc('t', out);
+ break;
+ default:
+ fputc(c, out);
+ }
+ }
+}
+
+static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
+{
+ fz_page *page;
+ fz_display_list *list = NULL;
+ fz_device *dev = NULL;
+ int start;
+ fz_cookie cookie = { 0 };
+ int needshot = 0;
+
+ fz_var(list);
+ fz_var(dev);
+
+ if (showtime)
+ {
+ start = gettime();
+ }
+
+ fz_try(ctx)
+ {
+ page = fz_load_page(doc, pagenum - 1);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow_message(ctx, "cannot load page %d in file '%s'", pagenum, filename);
+ }
+
+ if (mujstest_file)
+ {
+ pdf_document *inter = pdf_specifics(doc);
+ pdf_widget *widget = NULL;
+
+ if (inter)
+ widget = pdf_first_widget(inter, (pdf_page *)page);
+
+ if (widget)
+ {
+ fprintf(mujstest_file, "GOTO %d\n", pagenum);
+ needshot = 1;
+ }
+ for (;widget; widget = pdf_next_widget(widget))
+ {
+ fz_rect rect;
+ int w, h, len;
+ int type = pdf_widget_get_type(widget);
+
+ pdf_bound_widget(widget, &rect);
+ w = (rect.x1 - rect.x0);
+ h = (rect.y1 - rect.y0);
+ ++mujstest_count;
+ switch (type)
+ {
+ default:
+ fprintf(mujstest_file, "%% UNKNOWN %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case PDF_WIDGET_TYPE_PUSHBUTTON:
+ fprintf(mujstest_file, "%% PUSHBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case PDF_WIDGET_TYPE_CHECKBOX:
+ fprintf(mujstest_file, "%% CHECKBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case PDF_WIDGET_TYPE_RADIOBUTTON:
+ fprintf(mujstest_file, "%% RADIOBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case PDF_WIDGET_TYPE_TEXT:
+ {
+ int maxlen = pdf_text_widget_max_len(inter, widget);
+ int texttype = pdf_text_widget_content_type(inter, widget);
+
+ /* If height is low, assume a single row, and base
+ * the width off that. */
+ if (h < 10)
+ {
+ w = (w+h-1) / (h ? h : 1);
+ h = 1;
+ }
+ /* Otherwise, if width is low, work off height */
+ else if (w < 10)
+ {
+ h = (w+h-1) / (w ? w : 1);
+ w = 1;
+ }
+ else
+ {
+ w = (w+9)/10;
+ h = (h+9)/10;
+ }
+ len = w*h;
+ if (len < 2)
+ len = 2;
+ if (len > maxlen)
+ len = maxlen;
+ fprintf(mujstest_file, "%% TEXT %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ switch (texttype)
+ {
+ default:
+ case PDF_WIDGET_CONTENT_UNRESTRAINED:
+ fprintf(mujstest_file, "TEXT %d ", mujstest_count);
+ escape_string(mujstest_file, len-3, lorem);
+ fprintf(mujstest_file, "\n");
+ break;
+ case PDF_WIDGET_CONTENT_NUMBER:
+ fprintf(mujstest_file, "TEXT %d\n", mujstest_count);
+ break;
+ case PDF_WIDGET_CONTENT_SPECIAL:
+#ifdef __MINGW32__
+ fprintf(mujstest_file, "TEXT %I64d\n", 46702919800LL + mujstest_count);
+#else
+ fprintf(mujstest_file, "TEXT %lld\n", 46702919800LL + mujstest_count);
+#endif
+ break;
+ case PDF_WIDGET_CONTENT_DATE:
+ fprintf(mujstest_file, "TEXT Jun %d 1979\n", 1 + ((13 + mujstest_count) % 30));
+ break;
+ case PDF_WIDGET_CONTENT_TIME:
+ ++mujstest_count;
+ fprintf(mujstest_file, "TEXT %02d:%02d\n", ((mujstest_count/60) % 24), mujstest_count % 60);
+ break;
+ }
+ break;
+ }
+ case PDF_WIDGET_TYPE_LISTBOX:
+ fprintf(mujstest_file, "%% LISTBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case PDF_WIDGET_TYPE_COMBOBOX:
+ fprintf(mujstest_file, "%% COMBOBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ }
+ fprintf(mujstest_file, "CLICK %0.2f %0.2f\n", (rect.x0+rect.x1)/2, (rect.y0+rect.y1)/2);
+ }
+ }
+
+ if (uselist)
+ {
+ fz_try(ctx)
+ {
+ list = fz_new_display_list(ctx);
+ dev = fz_new_list_device(ctx, list);
+ fz_run_page(doc, page, dev, &fz_identity, &cookie);
+ }
+ fz_always(ctx)
+ {
+ fz_free_device(dev);
+ dev = NULL;
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_display_list(ctx, list);
+ fz_free_page(doc, page);
+ fz_rethrow_message(ctx, "cannot draw page %d in file '%s'", pagenum, filename);
+ }
+ }
+
+ if (showxml)
+ {
+ fz_try(ctx)
+ {
+ dev = fz_new_trace_device(ctx);
+ if (list)
+ fz_run_display_list(list, dev, &fz_identity, &fz_infinite_rect, &cookie);
+ else
+ fz_run_page(doc, page, dev, &fz_identity, &cookie);
+ }
+ fz_always(ctx)
+ {
+ fz_free_device(dev);
+ dev = NULL;
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_display_list(ctx, list);
+ fz_free_page(doc, page);
+ fz_rethrow(ctx);
+ }
+ }
+
+ if (showtext)
+ {
+ fz_text_page *text = NULL;
+
+ fz_var(text);
+
+ fz_try(ctx)
+ {
+ text = fz_new_text_page(ctx);
+ dev = fz_new_text_device(ctx, sheet, text);
+ if (showtext == TEXT_HTML)
+ fz_disable_device_hints(dev, FZ_IGNORE_IMAGE);
+ if (list)
+ fz_run_display_list(list, dev, &fz_identity, &fz_infinite_rect, &cookie);
+ else
+ fz_run_page(doc, page, dev, &fz_identity, &cookie);
+ fz_free_device(dev);
+ dev = NULL;
+ if (showtext == TEXT_XML)
+ {
+ fz_print_text_page_xml(ctx, out, text);
+ }
+ else if (showtext == TEXT_HTML)
+ {
+ fz_analyze_text(ctx, sheet, text);
+ fz_print_text_page_html(ctx, out, text);
+ }
+ else if (showtext == TEXT_PLAIN)
+ {
+ fz_print_text_page(ctx, out, text);
+ fz_printf(out, "\f\n");
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_free_device(dev);
+ dev = NULL;
+ fz_free_text_page(ctx, text);
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_display_list(ctx, list);
+ fz_free_page(doc, page);
+ fz_rethrow(ctx);
+ }
+ }
+
+ if (showmd5 || showtime)
+ printf("page %s %d", filename, pagenum);
+
+ if (output && output_format == OUT_SVG)
+ {
+ float zoom;
+ fz_matrix ctm;
+ fz_rect bounds, tbounds;
+ char buf[512];
+ FILE *file;
+ fz_output *out;
+
+ sprintf(buf, output, pagenum);
+ file = fopen(buf, "wb");
+ if (file == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", buf, strerror(errno));
+ out = fz_new_output_with_file(ctx, file);
+
+ fz_bound_page(doc, page, &bounds);
+ zoom = resolution / 72;
+ fz_pre_rotate(fz_scale(&ctm, zoom, zoom), rotation);
+ tbounds = bounds;
+ fz_transform_rect(&tbounds, &ctm);
+
+ fz_try(ctx)
+ {
+ dev = fz_new_svg_device(ctx, out, tbounds.x1-tbounds.x0, tbounds.y1-tbounds.y0);
+ if (list)
+ fz_run_display_list(list, dev, &ctm, &tbounds, &cookie);
+ else
+ fz_run_page(doc, page, dev, &ctm, &cookie);
+ fz_free_device(dev);
+ dev = NULL;
+ }
+ fz_always(ctx)
+ {
+ fz_free_device(dev);
+ dev = NULL;
+ fz_close_output(out);
+ fclose(file);
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_display_list(ctx, list);
+ fz_free_page(doc, page);
+ fz_rethrow(ctx);
+ }
+ }
+
+ if ((output && output_format != OUT_SVG)|| showmd5 || showtime)
+ {
+ float zoom;
+ fz_matrix ctm;
+ fz_rect bounds, tbounds;
+ fz_irect ibounds;
+ fz_pixmap *pix = NULL;
+ int w, h;
+
+ fz_var(pix);
+
+ fz_bound_page(doc, page, &bounds);
+ zoom = resolution / 72;
+ fz_pre_scale(fz_rotate(&ctm, rotation), zoom, zoom);
+ tbounds = bounds;
+ fz_round_rect(&ibounds, fz_transform_rect(&tbounds, &ctm));
+
+ /* Make local copies of our width/height */
+ w = width;
+ h = height;
+
+ /* If a resolution is specified, check to see whether w/h are
+ * exceeded; if not, unset them. */
+ if (res_specified)
+ {
+ int t;
+ t = ibounds.x1 - ibounds.x0;
+ if (w && t <= w)
+ w = 0;
+ t = ibounds.y1 - ibounds.y0;
+ if (h && t <= h)
+ h = 0;
+ }
+
+ /* Now w or h will be 0 unless they need to be enforced. */
+ if (w || h)
+ {
+ float scalex = w / (tbounds.x1 - tbounds.x0);
+ float scaley = h / (tbounds.y1 - tbounds.y0);
+ fz_matrix scale_mat;
+
+ if (fit)
+ {
+ if (w == 0)
+ scalex = 1.0f;
+ if (h == 0)
+ scaley = 1.0f;
+ }
+ else
+ {
+ if (w == 0)
+ scalex = scaley;
+ if (h == 0)
+ scaley = scalex;
+ }
+ if (!fit)
+ {
+ if (scalex > scaley)
+ scalex = scaley;
+ else
+ scaley = scalex;
+ }
+ fz_scale(&scale_mat, scalex, scaley);
+ fz_concat(&ctm, &ctm, &scale_mat);
+ tbounds = bounds;
+ fz_transform_rect(&tbounds, &ctm);
+ }
+ fz_round_rect(&ibounds, &tbounds);
+ fz_rect_from_irect(&tbounds, &ibounds);
+
+ /* TODO: banded rendering and multi-page ppm */
+
+ fz_try(ctx)
+ {
+ int savealpha = (out_cs == CS_RGBA || out_cs == CS_GRAYALPHA);
+
+ pix = fz_new_pixmap_with_bbox(ctx, colorspace, &ibounds);
+ fz_pixmap_set_resolution(pix, resolution);
+
+ if (savealpha)
+ fz_clear_pixmap(ctx, pix);
+ else
+ fz_clear_pixmap_with_value(ctx, pix, 255);
+
+ dev = fz_new_draw_device(ctx, pix);
+ if (list)
+ fz_run_display_list(list, dev, &ctm, &tbounds, &cookie);
+ else
+ fz_run_page(doc, page, dev, &ctm, &cookie);
+ fz_free_device(dev);
+ dev = NULL;
+
+ if (invert)
+ fz_invert_pixmap(ctx, pix);
+ if (gamma_value != 1)
+ fz_gamma_pixmap(ctx, pix, gamma_value);
+
+ if (savealpha)
+ fz_unmultiply_pixmap(ctx, pix);
+
+ if (output)
+ {
+ char buf[512];
+ sprintf(buf, output, pagenum);
+ if (output_format == OUT_PGM || output_format == OUT_PPM || output_format == OUT_PNM)
+ fz_write_pnm(ctx, pix, buf);
+ else if (output_format == OUT_PAM)
+ fz_write_pam(ctx, pix, buf, savealpha);
+ else if (output_format == OUT_PNG)
+ fz_write_png(ctx, pix, buf, savealpha);
+ else if (output_format == OUT_PWG)
+ {
+ if (strstr(output, "%d") != NULL)
+ append = 0;
+ if (out_cs == CS_MONO)
+ {
+ fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL);
+ fz_write_pwg_bitmap(ctx, bit, buf, append, NULL);
+ fz_drop_bitmap(ctx, bit);
+ }
+ else
+ fz_write_pwg(ctx, pix, buf, append, NULL);
+ append = 1;
+ }
+ else if (output_format == OUT_PCL)
+ {
+ fz_pcl_options options;
+
+ fz_pcl_preset(ctx, &options, "ljet4");
+
+ if (strstr(output, "%d") != NULL)
+ append = 0;
+ if (out_cs == CS_MONO)
+ {
+ fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL);
+ fz_write_pcl_bitmap(ctx, bit, buf, append, &options);
+ fz_drop_bitmap(ctx, bit);
+ }
+ else
+ fz_write_pcl(ctx, pix, buf, append, &options);
+ append = 1;
+ }
+ else if (output_format == OUT_PBM) {
+ fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL);
+ fz_write_pbm(ctx, bit, buf);
+ fz_drop_bitmap(ctx, bit);
+ }
+ }
+
+ if (showmd5)
+ {
+ unsigned char digest[16];
+ int i;
+
+ fz_md5_pixmap(pix, digest);
+ printf(" ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", digest[i]);
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_free_device(dev);
+ dev = NULL;
+ fz_drop_pixmap(ctx, pix);
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_display_list(ctx, list);
+ fz_free_page(doc, page);
+ fz_rethrow(ctx);
+ }
+ }
+
+ if (list)
+ fz_drop_display_list(ctx, list);
+
+ fz_free_page(doc, page);
+
+ if (showtime)
+ {
+ int end = gettime();
+ int diff = end - start;
+
+ if (diff < timing.min)
+ {
+ timing.min = diff;
+ timing.minpage = pagenum;
+ timing.minfilename = filename;
+ }
+ if (diff > timing.max)
+ {
+ timing.max = diff;
+ timing.maxpage = pagenum;
+ timing.maxfilename = filename;
+ }
+ timing.total += diff;
+ timing.count ++;
+
+ printf(" %dms", diff);
+ }
+
+ if (showmd5 || showtime)
+ printf("\n");
+
+ fz_flush_warnings(ctx);
+
+ if (mujstest_file && needshot)
+ {
+ fprintf(mujstest_file, "SCREENSHOT\n");
+ }
+
+ if (cookie.errors)
+ errored = 1;
+}
+
+static void drawrange(fz_context *ctx, fz_document *doc, char *range)
+{
+ int page, spage, epage, pagecount;
+ char *spec, *dash;
+
+ pagecount = fz_count_pages(doc);
+ spec = fz_strsep(&range, ",");
+ while (spec)
+ {
+ dash = strchr(spec, '-');
+
+ if (dash == spec)
+ spage = epage = pagecount;
+ else
+ spage = epage = atoi(spec);
+
+ if (dash)
+ {
+ if (strlen(dash) > 1)
+ epage = atoi(dash + 1);
+ else
+ epage = pagecount;
+ }
+
+ spage = fz_clampi(spage, 1, pagecount);
+ epage = fz_clampi(epage, 1, pagecount);
+
+ if (spage < epage)
+ for (page = spage; page <= epage; page++)
+ drawpage(ctx, doc, page);
+ else
+ for (page = spage; page >= epage; page--)
+ drawpage(ctx, doc, page);
+
+ spec = fz_strsep(&range, ",");
+ }
+}
+
+static void drawoutline(fz_context *ctx, fz_document *doc)
+{
+ fz_outline *outline = fz_load_outline(doc);
+ fz_output *out = NULL;
+
+ fz_var(out);
+ fz_try(ctx)
+ {
+ out = fz_new_output_with_file(ctx, stdout);
+ if (showoutline > 1)
+ fz_print_outline_xml(ctx, out, outline);
+ else
+ fz_print_outline(ctx, out, outline);
+ }
+ fz_always(ctx)
+ {
+ fz_close_output(out);
+ fz_free_outline(ctx, outline);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static int
+parse_colorspace(const char *name)
+{
+ int i;
+
+ for (i = 0; i < nelem(cs_name_table); i++)
+ {
+ if (!strcmp(name, cs_name_table[i].name))
+ return cs_name_table[i].colorspace;
+ }
+ fprintf(stderr, "Unknown colorspace \"%s\"\n", name);
+ exit(1);
+ return -1;
+}
+
+int main(int argc, char **argv)
+{
+ char *password = "";
+ fz_document *doc = NULL;
+ int c;
+ fz_context *ctx;
+
+ fz_var(doc);
+
+ while ((c = fz_getopt(argc, argv, "lo:p:r:R:b:c:dgmtx5G:Iw:h:fij:")) != -1)
+ {
+ switch (c)
+ {
+ case 'o': output = fz_optarg; break;
+ case 'p': password = fz_optarg; break;
+ case 'r': resolution = atof(fz_optarg); res_specified = 1; break;
+ case 'R': rotation = atof(fz_optarg); break;
+ case 'b': alphabits = atoi(fz_optarg); break;
+ case 'l': showoutline++; break;
+ case 'm': showtime++; break;
+ case 't': showtext++; break;
+ case 'x': showxml++; break;
+ case '5': showmd5++; break;
+ case 'g': out_cs = CS_GRAY; break;
+ case 'd': uselist = 0; break;
+ case 'c': out_cs = parse_colorspace(fz_optarg); break;
+ case 'G': gamma_value = atof(fz_optarg); break;
+ case 'w': width = atof(fz_optarg); break;
+ case 'h': height = atof(fz_optarg); break;
+ case 'f': fit = 1; break;
+ case 'I': invert++; break;
+ case 'j': mujstest_filename = fz_optarg; break;
+ case 'i': ignore_errors = 1; break;
+ default: usage(); break;
+ }
+ }
+
+ if (fz_optind == argc)
+ usage();
+
+ if (!showtext && !showxml && !showtime && !showmd5 && !showoutline && !output && !mujstest_filename)
+ {
+ printf("nothing to do\n");
+ exit(0);
+ }
+
+ if (mujstest_filename)
+ {
+ if (strcmp(mujstest_filename, "-") == 0)
+ mujstest_file = stdout;
+ else
+ mujstest_file = fopen(mujstest_filename, "wb");
+ }
+
+ ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);
+ if (!ctx)
+ {
+ fprintf(stderr, "cannot initialise context\n");
+ exit(1);
+ }
+
+ fz_set_aa_level(ctx, alphabits);
+
+ /* Determine output type */
+ output_format = OUT_PNG;
+ if (output)
+ {
+ char *suffix = output;
+ int i;
+
+ for (i = 0; i < nelem(suffix_table); i++)
+ {
+ char *s = strstr(suffix, suffix_table[i].suffix);
+
+ if (s != NULL)
+ {
+ suffix = s+1;
+ output_format = suffix_table[i].format;
+ i = 0;
+ }
+ }
+ }
+
+ {
+ int i, j;
+
+ for (i = 0; i < nelem(format_cs_table); i++)
+ {
+ if (format_cs_table[i].format == output_format)
+ {
+ if (out_cs == CS_UNSET)
+ out_cs = format_cs_table[i].default_cs;
+ for (j = 0; j < nelem(format_cs_table[i].permitted_cs); j++)
+ {
+ if (format_cs_table[i].permitted_cs[j] == out_cs)
+ break;
+ }
+ if (j == nelem(format_cs_table[i].permitted_cs))
+ {
+ fprintf(stderr, "Unsupported colorspace for this format\n");
+ exit(1);
+ }
+ }
+ }
+ }
+
+ switch (out_cs)
+ {
+ case CS_MONO:
+ case CS_GRAY:
+ case CS_GRAYALPHA:
+ colorspace = fz_device_gray(ctx);
+ break;
+ case CS_RGB:
+ case CS_RGBA:
+ colorspace = fz_device_rgb(ctx);
+ break;
+ default:
+ fprintf(stderr, "Unknown colorspace!\n");
+ exit(1);
+ break;
+ }
+
+ timing.count = 0;
+ timing.total = 0;
+ timing.min = 1 << 30;
+ timing.max = 0;
+ timing.minpage = 0;
+ timing.maxpage = 0;
+ timing.minfilename = "";
+ timing.maxfilename = "";
+
+ if (showxml || showtext)
+ out = fz_new_output_with_file(ctx, stdout);
+
+ if (showxml || showtext == TEXT_XML)
+ fz_printf(out, "<?xml version=\"1.0\"?>\n");
+
+ if (showtext)
+ sheet = fz_new_text_sheet(ctx);
+
+ if (showtext == TEXT_HTML)
+ {
+ fz_printf(out, "<style>\n");
+ fz_printf(out, "body{background-color:gray;margin:12pt;}\n");
+ fz_printf(out, "div.page{background-color:white;margin:6pt;padding:6pt;}\n");
+ fz_printf(out, "div.block{border:1px solid gray;margin:6pt;padding:6pt;}\n");
+ fz_printf(out, "div.metaline{display:table;width:100%%}\n");
+ fz_printf(out, "div.line{display:table-row;padding:6pt}\n");
+ fz_printf(out, "div.cell{display:table-cell;padding-left:6pt;padding-right:6pt}\n");
+ fz_printf(out, "p{margin:0pt;padding:0pt;}\n");
+ fz_printf(out, "</style>\n");
+ fz_printf(out, "<body>\n");
+ }
+
+ fz_try(ctx)
+ {
+ while (fz_optind < argc)
+ {
+ fz_try(ctx)
+ {
+ filename = argv[fz_optind++];
+ files++;
+
+ fz_try(ctx)
+ {
+ doc = fz_open_document(ctx, filename);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow_message(ctx, "cannot open document: %s", filename);
+ }
+
+ if (fz_needs_password(doc))
+ {
+ if (!fz_authenticate_password(doc, password))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot authenticate password: %s", filename);
+ if (mujstest_file)
+ fprintf(mujstest_file, "PASSWORD %s\n", password);
+ }
+
+ if (mujstest_file)
+ {
+ fprintf(mujstest_file, "OPEN %s\n", filename);
+ }
+
+ if (showxml || showtext == TEXT_XML)
+ fz_printf(out, "<document name=\"%s\">\n", filename);
+
+ if (showoutline)
+ drawoutline(ctx, doc);
+
+ if (showtext || showxml || showtime || showmd5 || output || mujstest_file)
+ {
+ if (fz_optind == argc || !isrange(argv[fz_optind]))
+ drawrange(ctx, doc, "1-");
+ if (fz_optind < argc && isrange(argv[fz_optind]))
+ drawrange(ctx, doc, argv[fz_optind++]);
+ }
+
+ if (showxml || showtext == TEXT_XML)
+ fz_printf(out, "</document>\n");
+
+ fz_close_document(doc);
+ doc = NULL;
+ }
+ fz_catch(ctx)
+ {
+ if (!ignore_errors)
+ fz_rethrow(ctx);
+
+ fz_close_document(doc);
+ doc = NULL;
+ fz_warn(ctx, "ignoring error in '%s'", filename);
+ }
+ }
+ }
+ fz_catch(ctx)
+ {
+ fz_close_document(doc);
+ fprintf(stderr, "error: cannot draw '%s'\n", filename);
+ errored = 1;
+ }
+
+ if (showtext == TEXT_HTML)
+ {
+ fz_printf(out, "</body>\n");
+ fz_printf(out, "<style>\n");
+ fz_print_text_sheet(ctx, out, sheet);
+ fz_printf(out, "</style>\n");
+ }
+
+ if (showtext)
+ fz_free_text_sheet(ctx, sheet);
+
+ if (showxml || showtext)
+ {
+ fz_close_output(out);
+ out = NULL;
+ }
+
+ if (showtime && timing.count > 0)
+ {
+ if (files == 1)
+ {
+ printf("total %dms / %d pages for an average of %dms\n",
+ timing.total, timing.count, timing.total / timing.count);
+ printf("fastest page %d: %dms\n", timing.minpage, timing.min);
+ printf("slowest page %d: %dms\n", timing.maxpage, timing.max);
+ }
+ else
+ {
+ printf("total %dms / %d pages for an average of %dms in %d files\n",
+ timing.total, timing.count, timing.total / timing.count, files);
+ printf("fastest page %d: %dms (%s)\n", timing.minpage, timing.min, timing.minfilename);
+ printf("slowest page %d: %dms (%s)\n", timing.maxpage, timing.max, timing.maxfilename);
+ }
+ }
+
+ if (mujstest_file && mujstest_file != stdout)
+ fclose(mujstest_file);
+
+ fz_free_context(ctx);
+ return (errored != 0);
+}
+
+#ifdef _MSC_VER
+int wmain(int argc, wchar_t *wargv[])
+{
+ char **argv = fz_argv_from_wargv(argc, wargv);
+ int ret = main(argc, argv);
+ fz_free_argv(argc, argv);
+ return ret;
+}
+#endif