From c930c80f31a13c2aea907a0d8b4ec24271ca5276 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 11 Aug 2017 15:55:26 +0200 Subject: Add PCL document writer. Fix color PCL writing bugs introduced when making alpha channels in pixmaps optional. --- source/fitz/output-pcl.c | 231 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 171 insertions(+), 60 deletions(-) (limited to 'source/fitz/output-pcl.c') diff --git a/source/fitz/output-pcl.c b/source/fitz/output-pcl.c index bb3a82dd..32072518 100644 --- a/source/fitz/output-pcl.c +++ b/source/fitz/output-pcl.c @@ -210,6 +210,25 @@ static void copy_opts(fz_pcl_options *dst, const fz_pcl_options *src) *dst = *src; } +const char *fz_pcl_write_options_usage = + "PCL output options:\n" + "\tcolorspace=mono: render 1-bit black and white page\n" + "\tcolorspace=rgb: render full color page\n" + "\tpreset=generic|ljet4|dj500|fs600|lj|lj2|lj3|lj3d|lj4|lj4pl|lj4d|lp2563b|oce9050\n" + "\tspacing=0: No vertical spacing capability\n" + "\tspacing=1: PCL 3 spacing (*p+Y)\n" + "\tspacing=2: PCL 4 spacing (*bY)\n" + "\tspacing=3: PCL 5 spacing (*bY and clear seed row)\n" + "\tmode2: Enable mode 2 graphics compression\n" + "\tmode3: Enable mode 3 graphics compression\n" + "\teog_reset: End of graphics (*rB) resets all parameters\n" + "\thas_duplex: Duplex supported (&lS)\n" + "\thas_papersize: Papersize setting supported (&lA)\n" + "\thas_copies: Number of copies supported (&lX)\n" + "\tis_ljet4pjl: Disable/Enable HP 4PJL model-specific output\n" + "\tis_oce9050: Disable/Enable Oce 9050 model-specific output\n" + "\n"; + void fz_pcl_preset(fz_context *ctx, fz_pcl_options *opts, const char *preset) { if (preset == NULL || *preset == 0 || !strcmp(preset, "generic")) @@ -242,105 +261,103 @@ void fz_pcl_preset(fz_context *ctx, fz_pcl_options *opts, const char *preset) fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown preset '%s'", preset); } -void fz_pcl_option(fz_context *ctx, fz_pcl_options *opts, const char *option, int val) +fz_pcl_options * +fz_parse_pcl_options(fz_context *ctx, fz_pcl_options *opts, const char *args) { - if (opts == NULL) - return; + const char *val; - if (!strcmp(option, "spacing")) + memset(opts, 0, sizeof *opts); + + if (fz_has_option(ctx, args, "preset", &val)) + fz_pcl_preset(ctx, opts, val); + else + fz_pcl_preset(ctx, opts, "generic"); + + if (fz_has_option(ctx, args, "spacing", &val)) { - switch (val) + switch (atoi(val)) { - case 0: - opts->features &= ~PCL_ANY_SPACING; - break; - case 1: - opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL3_SPACING; - break; - case 2: - opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL4_SPACING; - break; - case 3: - opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL5_SPACING; - break; - default: - fz_throw(ctx, FZ_ERROR_GENERIC, "Unsupported PCL spacing %d (0-3 only)", val); + case 0: opts->features &= ~PCL_ANY_SPACING; break; + case 1: opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL3_SPACING; break; + case 2: opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL4_SPACING; break; + case 3: opts->features = (opts->features & ~PCL_ANY_SPACING) | PCL5_SPACING; break; + default: fz_throw(ctx, FZ_ERROR_GENERIC, "Unsupported PCL spacing %d (0-3 only)", atoi(val)); } } - else if (!strcmp(option, "mode2")) + if (fz_has_option(ctx, args, "mode2", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~PCL_MODE_2_COMPRESSION; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= PCL_MODE_2_COMPRESSION; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for mode2 value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for mode2 value"); } - else if (!strcmp(option, "mode3")) + if (fz_has_option(ctx, args, "mode3", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~PCL_MODE_3_COMPRESSION; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= PCL_MODE_3_COMPRESSION; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for mode3 value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for mode3 value"); } - else if (!strcmp(option, "eog_reset")) + if (fz_has_option(ctx, args, "eog_reset", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~PCL_END_GRAPHICS_DOES_RESET; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= PCL_END_GRAPHICS_DOES_RESET; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for eog_reset value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for eog_reset value"); } - else if (!strcmp(option, "has_duplex")) + if (fz_has_option(ctx, args, "has_duplex", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~PCL_HAS_DUPLEX; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= PCL_HAS_DUPLEX; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for has_duplex value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for has_duplex value"); } - else if (!strcmp(option, "has_papersize")) + if (fz_has_option(ctx, args, "has_papersize", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~PCL_CAN_SET_PAPER_SIZE; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= PCL_CAN_SET_PAPER_SIZE; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for has_papersize value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for has_papersize value"); } - else if (!strcmp(option, "has_copies")) + if (fz_has_option(ctx, args, "has_copies", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~PCL_CAN_PRINT_COPIES; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= PCL_CAN_PRINT_COPIES; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for has_papersize value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for has_papersize value"); } - else if (!strcmp(option, "is_ljet4pjl")) + if (fz_has_option(ctx, args, "is_ljet4pjl", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~HACK__IS_A_LJET4PJL; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= HACK__IS_A_LJET4PJL; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for is_ljet4pjl value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for is_ljet4pjl value"); } - else if (!strcmp(option, "is_oce9050")) + if (fz_has_option(ctx, args, "is_oce9050", &val)) { - if (val == 0) + if (fz_option_eq(val, "no")) opts->features &= ~HACK__IS_A_OCE9050; - else if (val == 1) + else if (fz_option_eq(val, "yes")) opts->features |= HACK__IS_A_OCE9050; else - fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 0 or 1 for is_oce9050 value"); + fz_throw(ctx, FZ_ERROR_GENERIC, "Expected 'yes' or 'no' for is_oce9050 value"); } - else - fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown pcl option '%s'", option); + + return opts; } static void @@ -602,8 +619,7 @@ static void guess_paper_size(fz_pcl_options *pcl, int w, int h, int xres, int yr pcl->orientation = rotated; } -/* Copy a line, removing the alpha, returning true if the line - * was blank. */ +/* Copy a line, returning true if the line was blank. */ static int line_is_blank(unsigned char *dst, const unsigned char *sp, int w) { @@ -614,7 +630,6 @@ line_is_blank(unsigned char *dst, const unsigned char *sp, int w) zero |= (*dst++ = *sp++); zero |= (*dst++ = *sp++); zero |= (*dst++ = *sp++); - sp++; } return zero == 0; @@ -720,11 +735,17 @@ color_pcl_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colors int w = writer->super.w; int h = writer->super.h; int n = writer->super.n; + int s = writer->super.s; + int a = writer->super.alpha; int xres = writer->super.xres; int yres = writer->super.yres; - if (n != 4 || writer->super.s != 0) - fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be rgb to write as pcl"); + if (a != 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "color PCL cannot write alpha channel"); + if (s != 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "color PCL cannot write spot colors"); + if (n != 3) + fz_throw(ctx, FZ_ERROR_GENERIC, "color PCL must be RGB"); writer->linebuf = fz_malloc(ctx, w * 3 * 2); writer->compbuf = fz_malloc(ctx, 32767); @@ -788,7 +809,7 @@ color_pcl_write_band(fz_context *ctx, fz_band_writer *writer_, int stride, int b return; ds = w * 3; - ss = w * 4; + ss = w * 3; prev = writer->prev; curr = writer->curr; @@ -1111,8 +1132,12 @@ mono_pcl_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorsp int max_mode_2_size; int max_mode_3_size; + if (writer->super.alpha != 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "mono PCL cannot write alpha channel"); if (writer->super.s != 0) - fz_throw(ctx, FZ_ERROR_GENERIC, "Mono PCL cannot write spot colors"); + fz_throw(ctx, FZ_ERROR_GENERIC, "mono PCL cannot write spot colors"); + if (writer->super.n != 1) + fz_throw(ctx, FZ_ERROR_GENERIC, "mono PCL must be grayscale"); line_size = (w + 7)/8; max_mode_2_size = line_size + (line_size/127) + 1; @@ -1371,3 +1396,89 @@ fz_save_bitmap_as_pcl(fz_context *ctx, fz_bitmap *bitmap, char *filename, int ap fz_catch(ctx) fz_rethrow(ctx); } + +/* High-level document writer interface */ + +typedef struct fz_pcl_writer_s fz_pcl_writer; + +struct fz_pcl_writer_s +{ + fz_document_writer super; + fz_draw_options draw; + fz_pcl_options pcl; + fz_pixmap *pixmap; + int mono; + fz_output *out; +}; + +static fz_device * +pcl_begin_page(fz_context *ctx, fz_document_writer *wri_, const fz_rect *mediabox) +{ + fz_pcl_writer *wri = (fz_pcl_writer*)wri_; + return fz_new_draw_device_with_options(ctx, &wri->draw, mediabox, &wri->pixmap); +} + +static void +pcl_end_page(fz_context *ctx, fz_document_writer *wri_, fz_device *dev) +{ + fz_pcl_writer *wri = (fz_pcl_writer*)wri_; + + fz_close_device(ctx, dev); + fz_drop_device(ctx, dev); + + if (wri->mono) + { + fz_bitmap *bitmap = fz_new_bitmap_from_pixmap(ctx, wri->pixmap, NULL); + fz_try(ctx) + fz_write_bitmap_as_pcl(ctx, wri->out, bitmap, &wri->pcl); + fz_always(ctx) + fz_drop_bitmap(ctx, bitmap); + fz_catch(ctx) + fz_rethrow(ctx); + } + else + { + fz_write_pixmap_as_pcl(ctx, wri->out, wri->pixmap, &wri->pcl); + } + + fz_drop_pixmap(ctx, wri->pixmap); + wri->pixmap = NULL; +} + +static void +pcl_close_writer(fz_context *ctx, fz_document_writer *wri_) +{ +} + +static void +pcl_drop_writer(fz_context *ctx, fz_document_writer *wri_) +{ + fz_pcl_writer *wri = (fz_pcl_writer*)wri_; + fz_drop_pixmap(ctx, wri->pixmap); + fz_drop_output(ctx, wri->out); +} + +fz_document_writer * +fz_new_pcl_writer(fz_context *ctx, const char *path, const char *options) +{ + fz_pcl_writer *wri = fz_new_derived_document_writer(ctx, fz_pcl_writer, pcl_begin_page, pcl_end_page, pcl_close_writer, pcl_drop_writer); + const char *val; + + fz_try(ctx) + { + fz_parse_draw_options(ctx, &wri->draw, options); + fz_parse_pcl_options(ctx, &wri->pcl, options); + if (fz_has_option(ctx, options, "colorspace", &val)) + if (fz_option_eq(val, "mono")) + wri->mono = 1; + wri->out = fz_new_output_with_path(ctx, path ? path : "out.pcl", 0); + } + fz_catch(ctx) + { + fz_drop_output(ctx, wri->out); + fz_free(ctx, wri); + fz_rethrow(ctx); + } + + return (fz_document_writer*)wri; +} -- cgit v1.2.3