summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-05-22 17:12:43 +0100
committerRobin Watts <robin.watts@artifex.com>2013-06-08 21:56:58 -0400
commit60005383aae11f8b8fabd8926e439e7f0bdc29de (patch)
treee8127354528a2b5778e615359e1c074d3daabc57
parentb580657f6cf727a01248254fc11952a6b611c1d2 (diff)
downloadmupdf-60005383aae11f8b8fabd8926e439e7f0bdc29de.tar.xz
Simple PCL output
Mono only for now. Copy the appropriate fz_pcl_options for the printer required and pass that to fz_write_pcl_bitmap. Add pcl output to mudraw - .pcl produces laserjet 4 compatible pcl.
-rw-r--r--apps/mudraw.c29
-rw-r--r--fitz/fitz.h92
-rw-r--r--fitz/res_pcl.c856
-rw-r--r--fitz/res_pixmap.c7
-rw-r--r--win32/libmupdf.vcproj4
5 files changed, 984 insertions, 4 deletions
diff --git a/apps/mudraw.c b/apps/mudraw.c
index 5b83f353..cab25ac0 100644
--- a/apps/mudraw.c
+++ b/apps/mudraw.c
@@ -12,7 +12,7 @@
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 };
+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 };
@@ -31,7 +31,8 @@ static const suffix_t suffix_table[] =
{ ".pam", OUT_PAM },
{ ".pbm", OUT_PBM },
{ ".svg", OUT_SVG },
- { ".pwg", OUT_PWG }
+ { ".pwg", OUT_PWG },
+ { ".pcl", OUT_PCL }
};
typedef struct
@@ -71,7 +72,8 @@ static const format_cs_table_t format_cs_table[] =
{ 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_PWG, CS_RGB, { CS_MONO, CS_GRAY, CS_RGB } },
+ { OUT_PCL, CS_MONO, { CS_MONO } }
};
/*
@@ -591,7 +593,8 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
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
@@ -637,6 +640,24 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
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);
diff --git a/fitz/fitz.h b/fitz/fitz.h
index d45bd7e2..c89dc618 100644
--- a/fitz/fitz.h
+++ b/fitz/fitz.h
@@ -1605,6 +1605,8 @@ int fz_pixmap_components(fz_context *ctx, fz_pixmap *pix);
*/
unsigned char *fz_pixmap_samples(fz_context *ctx, fz_pixmap *pix);
+void fz_pixmap_set_resolution(fz_pixmap *pix, int res);
+
/*
fz_clear_pixmap_with_value: Clears a pixmap with the given value.
@@ -3468,4 +3470,94 @@ enum
*/
void fz_write_document(fz_document *doc, char *filename, fz_write_options *opts);
+/*
+ PCL output
+*/
+typedef struct fz_pcl_options_s fz_pcl_options;
+
+struct fz_pcl_options_s
+{
+ /* Features of a particular printer */
+ int features;
+ const char *odd_page_init;
+ const char *even_page_init;
+
+ /* Options for this job */
+ int tumble;
+ int duplex_set;
+ int duplex;
+ int paper_size;
+ int manual_feed_set;
+ int manual_feed;
+ int media_position_set;
+ int media_position;
+
+ /* Updated as we move through the job */
+ int page_count;
+};
+
+/*
+ fz_pcl_preset: Retrieve a set of fz_pcl_options suitable for a given
+ preset.
+
+ opts: pointer to options structure to populate.
+
+ preset: Preset to fetch. Currently defined presets include:
+ ljet4 HP DeskJet
+ dj500 HP DeskJet 500
+ fs600 Kyocera FS-600
+ lj HP LaserJet, HP LaserJet Plus
+ lj2 HP LaserJet IIp, HP LaserJet IId
+ lj3 HP LaserJet III
+ lj3d HP LaserJet IIId
+ lj4 HP LaserJet 4
+ lj4pl HP LaserJet 4 PL
+ lj4d HP LaserJet 4d
+ lp2563b HP 2563B line printer
+ oce9050 Oce 9050 Line printer
+
+ Throws exception on unknown preset.
+*/
+void fz_pcl_preset(fz_context *ctx, fz_pcl_options *opts, const char *preset);
+
+/*
+ fz_pcl_option: Set a given PCL option to a given value in the supplied
+ options structure.
+
+ opts: The option structure to modify,
+
+ option: The option to change.
+
+ val: The value that the option should be set to. Acceptable ranges of
+ values depend on the option in question.
+
+ Throws an exception on attempt to set an unknown option, or an illegal
+ value.
+
+ Currently defined options/values are as follows:
+
+ spacing,0 No vertical spacing capability
+ spacing,1 PCL 3 spacing (<ESC>*p+<n>Y)
+ spacing,2 PCL 4 spacing (<ESC>*b<n>Y)
+ spacing,3 PCL 5 spacing (<ESC>*b<n>Y and clear seed row)
+ mode2,0 or 1 Disable/Enable mode 2 graphics compression
+ mode3,0 or 1 Disable/Enable mode 3 graphics compression
+ mode3,0 or 1 Disable/Enable mode 3 graphics compression
+ eog_reset,0 or 1 End of graphics (<ESC>*rB) resets all parameters
+ has_duplex,0 or 1 Duplex supported (<ESC>&l<duplex>S)
+ has_papersize,0 or 1 Papersize setting supported (<ESC>&l<sizecode>A)
+ has_copies,0 or 1 Number of copies supported (<ESC>&l<copies>X)
+ is_ljet4pjl,0 or 1 Disable/Enable HP 4PJL model-specific output
+ is_oce9050,0 or 1 Disable/Enable Oce 9050 model-specific output
+*/
+void fz_pcl_option(fz_context *ctx, fz_pcl_options *opts, const char *option, int val);
+
+void fz_output_pcl(fz_output *out, const fz_pixmap *pixmap, fz_pcl_options *pcl);
+
+void fz_output_pcl_bitmap(fz_output *out, const fz_bitmap *bitmap, fz_pcl_options *pcl);
+
+void fz_write_pcl(fz_context *ctx, fz_pixmap *pixmap, char *filename, int append, fz_pcl_options *pcl);
+
+void fz_write_pcl_bitmap(fz_context *ctx, fz_bitmap *bitmap, char *filename, int append, fz_pcl_options *pcl);
+
#endif
diff --git a/fitz/res_pcl.c b/fitz/res_pcl.c
new file mode 100644
index 00000000..b9fd8f16
--- /dev/null
+++ b/fitz/res_pcl.c
@@ -0,0 +1,856 @@
+#include "fitz-internal.h"
+
+/* Lifted from ghostscript gdevjlm.h */
+/*
+ * The notion that there is such a thing as a "PCL printer" is a fiction: no
+ * two "PCL" printers, even at the same PCL level, have identical command
+ * sets. (The H-P documentation isn't fully accurate either; for example,
+ * it doesn't reveal that the DeskJet printers implement anything beyond PCL
+ * 3.)
+ *
+ * This file contains feature definitions for a generic monochrome PCL
+ * driver (gdevdljm.c), and the specific feature values for all such
+ * printers that Ghostscript currently supports.
+ */
+
+/* Printer spacing capabilities. Include at most one of these. */
+#define PCL_NO_SPACING 0 /* no vertical spacing capability, must be 0 */
+#define PCL3_SPACING 1 /* <ESC>*p+<n>Y (PCL 3) */
+#define PCL4_SPACING 2 /* <ESC>*b<n>Y (PCL 4) */
+#define PCL5_SPACING 4 /* <ESC>*b<n>Y and clear seed row (PCL 5) */
+/* The following is only used internally. */
+#define PCL_ANY_SPACING\
+ (PCL3_SPACING | PCL4_SPACING | PCL5_SPACING)
+
+/* Individual printer properties. Any subset of these may be included. */
+#define PCL_MODE_2_COMPRESSION 8 /* compression mode 2 supported */
+ /* (PCL 4) */
+#define PCL_MODE_3_COMPRESSION 16 /* compression modes 2 & 3 supported */
+ /* (PCL 5) */
+#define PCL_END_GRAPHICS_DOES_RESET 32 /* <esc>*rB resets all parameters */
+#define PCL_HAS_DUPLEX 64 /* <esc>&l<duplex>S supported */
+#define PCL_CAN_SET_PAPER_SIZE 128 /* <esc>&l<sizecode>A supported */
+#define PCL_CAN_PRINT_COPIES 256 /* <esc>&l<copies>X supported */
+#define HACK__IS_A_LJET4PJL 512
+#define HACK__IS_A_OCE9050 1024
+
+/* Shorthands for the most common spacing/compression combinations. */
+#define PCL_MODE0 PCL3_SPACING
+#define PCL_MODE0NS PCL_NO_SPACING
+#define PCL_MODE2 (PCL4_SPACING | PCL_MODE_2_COMPRESSION)
+#define PCL_MODE2P (PCL_NO_SPACING | PCL_MODE_2_COMPRESSION)
+#define PCL_MODE3 (PCL5_SPACING | PCL_MODE_3_COMPRESSION)
+#define PCL_MODE3NS (PCL_NO_SPACING | PCL_MODE_3_COMPRESSION)
+
+#define MIN_SKIP_LINES 7
+static const char *const from2to3 = "\033*b3M";
+static const char *const from3to2 = "\033*b2M";
+static const int penalty_from2to3 = 5; /* strlen(from2to3); */
+static const int penalty_from3to2 = 5; /* strlen(from3to2); */
+
+/* H-P DeskJet */
+static const fz_pcl_options fz_pcl_options_ljet4 =
+{
+ (PCL_MODE2 | PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE),
+ "\033&k1W\033*b2M",
+ "\033&k1W\033*b2M"
+};
+
+/* H-P DeskJet 500 */
+static const fz_pcl_options fz_pcl_options_dj500 =
+{
+ (PCL_MODE3 | PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE),
+ "\033&k1W",
+ "\033&k1W"
+};
+
+/* Kyocera FS-600 */
+static const fz_pcl_options fz_pcl_options_fs600 =
+{
+ (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
+ "\033*r0F\033&u%dD",
+ "\033*r0F\033&u%dD"
+};
+
+/* H-P original LaserJet */
+/* H-P LaserJet Plus */
+static const fz_pcl_options fz_pcl_options_lj =
+{
+ (PCL_MODE0),
+ "\033*b0M",
+ "\033*b0M"
+};
+
+/* H-P LaserJet IIp, IId */
+static const fz_pcl_options fz_pcl_options_lj2 =
+{
+ (PCL_MODE2P | PCL_CAN_SET_PAPER_SIZE),
+ "\033*r0F\033*b2M",
+ "\033*r0F\033*b2M"
+};
+
+/* H-P LaserJet III* */
+static const fz_pcl_options fz_pcl_options_lj3 =
+{
+ (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
+ "\033&l-180u36Z\033*r0F",
+ "\033&l-180u36Z\033*r0F"
+};
+
+/* H-P LaserJet IIId */
+static const fz_pcl_options fz_pcl_options_lj3d =
+{
+ (PCL_MODE3 | PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
+ "\033&l-180u36Z\033*r0F",
+ "\033&l180u36Z\033*r0F"
+};
+
+/* H-P LaserJet 4 */
+static const fz_pcl_options fz_pcl_options_lj4 =
+{
+ (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
+ "\033&l-180u36Z\033*r0F\033&u%dD",
+ "\033&l-180u36Z\033*r0F\033&u%dD"
+};
+
+/* H-P LaserJet 4 PL */
+static const fz_pcl_options fz_pcl_options_lj4pl =
+{
+ (PCL_MODE3 | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES | HACK__IS_A_LJET4PJL),
+ "\033&l-180u36Z\033*r0F\033&u%dD",
+ "\033&l-180u36Z\033*r0F\033&u%dD"
+};
+
+/* H-P LaserJet 4d */
+static const fz_pcl_options fz_pcl_options_lj4d =
+{
+ (PCL_MODE3 | PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES),
+ "\033&l-180u36Z\033*r0F\033&u%dD",
+ "\033&l180u36Z\033*r0F\033&u%dD"
+};
+
+/* H-P 2563B line printer */
+static const fz_pcl_options fz_pcl_options_lp2563b =
+{
+ (PCL_MODE0NS | PCL_CAN_SET_PAPER_SIZE),
+ "\033*b0M",
+ "\033*b0M"
+};
+
+/* OCE 9050 line printer */
+static const fz_pcl_options fz_pcl_options_oce9050 =
+{
+ (PCL_MODE3NS | PCL_CAN_SET_PAPER_SIZE | HACK__IS_A_OCE9050),
+ "\033*b0M",
+ "\033*b0M"
+};
+
+static void copy_opts(fz_pcl_options *dst, const fz_pcl_options *src)
+{
+ if (dst)
+ *dst = *src;
+}
+
+void fz_pcl_preset(fz_context *ctx, fz_pcl_options *opts, const char *preset)
+{
+ if (preset == NULL || *preset == 0 || !strcmp(preset, "ljet4"))
+ copy_opts(opts, &fz_pcl_options_ljet4);
+ else if (!strcmp(preset, "dj500"))
+ copy_opts(opts, &fz_pcl_options_dj500);
+ else if (!strcmp(preset, "fs600"))
+ copy_opts(opts, &fz_pcl_options_fs600);
+ else if (!strcmp(preset, "lj"))
+ copy_opts(opts, &fz_pcl_options_lj);
+ else if (!strcmp(preset, "lj2"))
+ copy_opts(opts, &fz_pcl_options_lj2);
+ else if (!strcmp(preset, "lj3"))
+ copy_opts(opts, &fz_pcl_options_lj3);
+ else if (!strcmp(preset, "lj3d"))
+ copy_opts(opts, &fz_pcl_options_lj3d);
+ else if (!strcmp(preset, "lj4"))
+ copy_opts(opts, &fz_pcl_options_lj4);
+ else if (!strcmp(preset, "lj4pl"))
+ copy_opts(opts, &fz_pcl_options_lj4pl);
+ else if (!strcmp(preset, "lj4d"))
+ copy_opts(opts, &fz_pcl_options_lj4d);
+ else if (!strcmp(preset, "lp2563b"))
+ copy_opts(opts, &fz_pcl_options_lp2563b);
+ else if (!strcmp(preset, "oce9050"))
+ copy_opts(opts, &fz_pcl_options_oce9050);
+ else
+ fz_throw(ctx, "Unknown preset '%s'", preset);
+}
+
+void fz_pcl_option(fz_context *ctx, fz_pcl_options *opts, const char *option, int val)
+{
+ if (opts == NULL)
+ return;
+
+ if (!strcmp(option, "spacing"))
+ {
+ switch (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, "Unsupported PCL spacing %d (0-3 only)", val);
+ }
+ }
+ else if (!strcmp(option, "mode2"))
+ {
+ if (val == 0)
+ opts->features &= ~PCL_MODE_2_COMPRESSION;
+ else if (val == 1)
+ opts->features |= PCL_MODE_2_COMPRESSION;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for mode2 value");
+ }
+ else if (!strcmp(option, "mode3"))
+ {
+ if (val == 0)
+ opts->features &= ~PCL_MODE_3_COMPRESSION;
+ else if (val == 1)
+ opts->features |= PCL_MODE_3_COMPRESSION;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for mode3 value");
+ }
+ else if (!strcmp(option, "eog_reset"))
+ {
+ if (val == 0)
+ opts->features &= ~PCL_END_GRAPHICS_DOES_RESET;
+ else if (val == 1)
+ opts->features |= PCL_END_GRAPHICS_DOES_RESET;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for eog_reset value");
+ }
+ else if (!strcmp(option, "has_duplex"))
+ {
+ if (val == 0)
+ opts->features &= ~PCL_HAS_DUPLEX;
+ else if (val == 1)
+ opts->features |= PCL_HAS_DUPLEX;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for has_duplex value");
+ }
+ else if (!strcmp(option, "has_papersize"))
+ {
+ if (val == 0)
+ opts->features &= ~PCL_CAN_SET_PAPER_SIZE;
+ else if (val == 1)
+ opts->features |= PCL_CAN_SET_PAPER_SIZE;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for has_papersize value");
+ }
+ else if (!strcmp(option, "has_copies"))
+ {
+ if (val == 0)
+ opts->features &= ~PCL_CAN_PRINT_COPIES;
+ else if (val == 1)
+ opts->features |= PCL_CAN_PRINT_COPIES;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for has_papersize value");
+ }
+ else if (!strcmp(option, "is_ljet4pjl"))
+ {
+ if (val == 0)
+ opts->features &= ~HACK__IS_A_LJET4PJL;
+ else if (val == 1)
+ opts->features |= HACK__IS_A_LJET4PJL;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for is_ljet4pjl value");
+ }
+ else if (!strcmp(option, "is_oce9050"))
+ {
+ if (val == 0)
+ opts->features &= ~HACK__IS_A_OCE9050;
+ else if (val == 1)
+ opts->features |= HACK__IS_A_OCE9050;
+ else
+ fz_throw(ctx, "Expected 0 or 1 for is_oce9050 value");
+ }
+ else
+ fz_throw(ctx, "Unknown pcl option '%s'", option);
+}
+
+static void
+make_init(fz_pcl_options *pcl, char *buf, unsigned long len, const char *str, int res)
+{
+ int paper_source = -1;
+
+ snprintf(buf, len, str, res);
+
+ if (pcl->manual_feed_set && pcl->manual_feed)
+ paper_source = 2;
+ else if (pcl->media_position_set && pcl->media_position >= 0)
+ paper_source = pcl->media_position;
+ if (paper_source >= 0)
+ {
+ char buf2[40];
+ snprintf(buf2, sizeof(buf2), "\033&l%dH", paper_source);
+ strncat(buf, buf2, len);
+ }
+}
+
+static void
+pcl_header(fz_output *out, fz_pcl_options *pcl, int num_copies, int xres)
+{
+ char odd_page_init[80];
+ char even_page_init[80];
+
+ make_init(pcl, odd_page_init, sizeof(odd_page_init), pcl->odd_page_init, xres);
+ make_init(pcl, even_page_init, sizeof(even_page_init), pcl->even_page_init, xres);
+
+ if (pcl->page_count == 0)
+ {
+ if (pcl->features & HACK__IS_A_LJET4PJL)
+ fz_puts(out, "\033%-12345X@PJL\r\n@PJL ENTER LANGUAGE = PCL\r\n");
+ fz_puts(out, "\033E"); /* reset printer */
+ /* If the printer supports it, set the paper size */
+ /* based on the actual requested size. */
+ if (pcl->features & PCL_CAN_SET_PAPER_SIZE)
+ fz_printf(out, "\033&l%dA", pcl->paper_size);
+ /* If printer can duplex, set duplex mode appropriately. */
+ if (pcl->features & PCL_HAS_DUPLEX)
+ {
+ if (pcl->duplex_set)
+ {
+ if (pcl->duplex)
+ {
+ if (!pcl->tumble)
+ fz_puts(out, "\033&l1S");
+ else
+ fz_puts(out, "\033&l2S");
+ }
+ else
+ fz_puts(out, "\033&l0S");
+ }
+ else
+ {
+ /* default to duplex for this printer */
+ fz_puts(out, "\033&l1S");
+ }
+ }
+ }
+
+ /* Put out per-page initialization. */
+ /* in duplex mode the sheet is already in process, so there are some
+ * commands which must not be sent to the printer for the 2nd page,
+ * as this commands will cause the printer to eject the sheet with
+ * only the 1st page printed. This commands are:
+ * \033&l%dA (setting paper size)
+ * \033&l%dH (setting paper tray)
+ * in simplex mode we set this parameters for each page,
+ * in duplex mode we set this parameters for each odd page
+ */
+
+ if ((pcl->features & PCL_HAS_DUPLEX) && pcl->duplex_set && pcl->duplex)
+ {
+ /* We are printing duplex, so change margins as needed */
+ if (((pcl->page_count/num_copies)%2) == 0)
+ {
+ if (pcl->page_count != 0 && (pcl->features & PCL_CAN_SET_PAPER_SIZE))
+ {
+ fz_printf(out, "\033&l%dA", pcl->paper_size);
+ }
+ fz_puts(out, "\033&l0o0l0E");
+ fz_puts(out, pcl->odd_page_init);
+ }
+ else
+ fz_puts(out, pcl->even_page_init);
+ }
+ else
+ {
+ if (pcl->features & PCL_CAN_SET_PAPER_SIZE)
+ {
+ fz_printf(out, "\033&l%dA", pcl->paper_size);
+ }
+ fz_puts(out, "\033&l0o0l0E");
+ fz_puts(out, pcl->odd_page_init);
+ }
+
+ fz_printf(out, "\033&l%dX", num_copies); /* # of copies */
+
+ /* End raster graphics, position cursor at top. */
+ fz_puts(out, "\033*rB\033*p0x0Y");
+
+ /* The DeskJet and DeskJet Plus reset everything upon */
+ /* receiving \033*rB, so we must reinitialize graphics mode. */
+ if (pcl->features & PCL_END_GRAPHICS_DOES_RESET)
+ {
+ fz_puts(out, pcl->odd_page_init); /* Assume this does the right thing */
+ fz_printf(out, "\033&l%dX", num_copies); /* # of copies */
+ }
+
+ /* Set resolution. */
+ fz_printf(out, "\033*t%dR", xres);
+ pcl->page_count++;
+}
+
+void
+fz_output_pcl(fz_output *out, const fz_pixmap *pixmap, fz_pcl_options *pcl)
+{
+ //unsigned char *sp;
+ //int y, x, sn, dn, ss;
+ fz_context *ctx;
+
+ if (!out || !pixmap)
+ return;
+
+ ctx = out->ctx;
+
+ if (pixmap->n != 1 && pixmap->n != 2 && pixmap->n != 4)
+ fz_throw(ctx, "pixmap must be grayscale or rgb to write as pcl");
+
+ pcl_header(out, pcl, 1, pixmap->xres);
+
+#if 0
+ sn = pixmap->n;
+ dn = pixmap->n;
+ if (dn == 2 || dn == 4)
+ dn--;
+
+ /* Now output the actual bitmap, using a packbits like compression */
+ sp = pixmap->samples;
+ ss = pixmap->w * sn;
+ y = 0;
+ while (y < pixmap->h)
+ {
+ int yrep;
+
+ assert(sp == pixmap->samples + y * ss);
+
+ /* Count the number of times this line is repeated */
+ for (yrep = 1; yrep < 256 && y+yrep < pixmap->h; yrep++)
+ {
+ if (memcmp(sp, sp + yrep * ss, ss) != 0)
+ break;
+ }
+ fz_write_byte(out, yrep-1);
+
+ /* Encode the line */
+ x = 0;
+ while (x < pixmap->w)
+ {
+ int d;
+
+ assert(sp == pixmap->samples + y * ss + x * sn);
+
+ /* How far do we have to look to find a repeated value? */
+ for (d = 1; d < 128 && x+d < pixmap->w; d++)
+ {
+ if (memcmp(sp + (d-1)*sn, sp + d*sn, sn) == 0)
+ break;
+ }
+ if (d == 1)
+ {
+ int xrep;
+
+ /* We immediately have a repeat (or we've hit
+ * the end of the line). Count the number of
+ * times this value is repeated. */
+ for (xrep = 1; xrep < 128 && x+xrep < pixmap->w; xrep++)
+ {
+ if (memcmp(sp, sp + xrep*sn, sn) != 0)
+ break;
+ }
+ fz_write_byte(out, xrep-1);
+ fz_write(out, sp, dn);
+ sp += sn*xrep;
+ x += xrep;
+ }
+ else
+ {
+ fz_write_byte(out, 257-d);
+ x += d;
+ while (d > 0)
+ {
+ fz_write(out, sp, dn);
+ sp += sn;
+ d--;
+ }
+ }
+ }
+
+ /* Move to the next line */
+ sp += ss*(yrep-1);
+ y += yrep;
+ }
+#endif
+}
+
+/*
+ * Mode 2 Row compression routine for the HP DeskJet & LaserJet IIp.
+ * Compresses data from row up to end_row, storing the result
+ * starting at compressed. Returns the number of bytes stored.
+ * Runs of K<=127 literal bytes are encoded as K-1 followed by
+ * the bytes; runs of 2<=K<=127 identical bytes are encoded as
+ * 257-K followed by the byte.
+ * In the worst case, the result is N+(N/127)+1 bytes long,
+ * where N is the original byte count (end_row - row).
+ */
+int
+mode2compress(unsigned char *out, unsigned char *in, int in_len)
+{
+ int x;
+ int out_len = 0;
+ int run;
+
+ for (x = 0; x < in_len; x += run)
+ {
+ /* How far do we have to look to find a value that isn't repeated? */
+ for (run = 1; run < 127 && x+run < in_len; run++)
+ if (in[0] != in[run])
+ break;
+ if (run > 1)
+ {
+ /* We have a run of matching bytes */
+ out[out_len++] = 257-run;
+ out[out_len++] = in[0];
+ }
+ else
+ {
+ int i;
+
+ /* How many literals do we need to copy? */
+ for (run = 1; run < 127 && x+run < in_len; run++)
+ if (in[run] == in[run+1])
+ break;
+ out[out_len++] = run-1;
+ for (i = 0; i < run; i++)
+ out[out_len++] = in[i];
+ }
+ in += run;
+ }
+ return out_len;
+}
+
+/*
+ * Mode 3 compression routine for the HP LaserJet III family.
+ * Compresses bytecount bytes starting at current, storing the result
+ * in compressed, comparing against and updating previous.
+ * Returns the number of bytes stored. In the worst case,
+ * the number of bytes is bytecount+(bytecount/8)+1.
+ */
+int
+mode3compress(unsigned char *out, const unsigned char *in, unsigned char *prev, int in_len)
+{
+ unsigned char *compressed = out;
+ const unsigned char *cur = in;
+ const unsigned char *end = in + in_len;
+
+ while (cur < end) { /* Detect a maximum run of unchanged bytes. */
+ const unsigned char *run = cur;
+ const unsigned char *diff;
+ const unsigned char *stop;
+ int offset, cbyte;
+
+ while (cur < end && *cur == *prev) {
+ cur++, prev++;
+ }
+ if (cur == end)
+ break; /* rest of row is unchanged */
+ /* Detect a run of up to 8 changed bytes. */
+ /* We know that *cur != *prev. */
+ diff = cur;
+ stop = (end - cur > 8 ? cur + 8 : end);
+ do
+ {
+ *prev++ = *cur++;
+ }
+ while (cur < stop && *cur != *prev);
+ /* Now [run..diff) are unchanged, and */
+ /* [diff..cur) are changed. */
+ /* Generate the command byte(s). */
+ offset = diff - run;
+ cbyte = (cur - diff - 1) << 5;
+ if (offset < 31)
+ *out++ = cbyte + offset;
+ else {
+ *out++ = cbyte + 31;
+ offset -= 31;
+ while (offset >= 255)
+ *out++ = 255, offset -= 255;
+ *out++ = offset;
+ }
+ /* Copy the changed data. */
+ while (diff < cur)
+ *out++ = *diff++;
+ }
+ return out - compressed;
+}
+
+void wind(void)
+{}
+
+
+void
+fz_output_pcl_bitmap(fz_output *out, const fz_bitmap *bitmap, fz_pcl_options *pcl)
+{
+ unsigned char *data, *out_data;
+ int y, ss, rmask, line_size;
+ fz_context *ctx;
+ int num_blank_lines;
+ int compression = -1;
+ unsigned char *prev_row = NULL;
+ unsigned char *out_row_mode_2 = NULL;
+ unsigned char *out_row_mode_3 = NULL;
+ int out_count;
+ int max_mode_2_size;
+ int max_mode_3_size;
+
+ if (!out || !bitmap)
+ return;
+
+ ctx = out->ctx;
+
+ if (pcl->features & HACK__IS_A_OCE9050)
+ {
+ /* Enter HPGL/2 mode, begin plot, Initialise (start plot), Enter PCL mode */
+ fz_puts(out, "\033%1BBPIN;\033%1A");
+ }
+
+ pcl_header(out, pcl, 1, bitmap->xres);
+
+ fz_var(prev_row);
+ fz_var(out_row_mode_2);
+ fz_var(out_row_mode_3);
+
+ fz_try(ctx)
+ {
+ num_blank_lines = 0;
+ rmask = ~0 << (-bitmap->w & 7);
+ line_size = (bitmap->w + 7)/8;
+ max_mode_2_size = line_size + (line_size/127) + 1;
+ max_mode_3_size = line_size + (line_size/8) + 1;
+ prev_row = fz_calloc(ctx, line_size, sizeof(unsigned char));
+ out_row_mode_2 = fz_calloc(ctx, max_mode_2_size, sizeof(unsigned char));
+ out_row_mode_3 = fz_calloc(ctx, max_mode_3_size, sizeof(unsigned char));
+
+ /* Transfer raster graphics. */
+ data = bitmap->samples;
+ ss = bitmap->stride;
+ for (y = 0; y < bitmap->h; y++, data += ss)
+ {
+ unsigned char *end_data = data + line_size;
+
+ if ((end_data[-1] & rmask) == 0)
+ {
+ end_data--;
+ while (end_data > data && end_data[-1] == 0)
+ end_data--;
+ }
+ if (end_data == data)
+ {
+ /* Blank line */
+ num_blank_lines++;
+ continue;
+ }
+ wind();
+ /* We've reached a non-blank line. */
+ /* Put out a spacing command if necessary. */
+ if (num_blank_lines == y) {
+ /* We're at the top of a page. */
+ if (pcl->features & PCL_ANY_SPACING)
+ {
+ if (num_blank_lines > 0)
+ fz_printf(out, "\033*p+%dY", num_blank_lines * bitmap->yres);
+ /* Start raster graphics. */
+ fz_puts(out, "\033*r1A");
+ }
+ else if (pcl->features & PCL_MODE_3_COMPRESSION)
+ {
+ /* Start raster graphics. */
+ fz_puts(out, "\033*r1A");
+ for (; num_blank_lines; num_blank_lines--)
+ fz_puts(out, "\033*b0W");
+ }
+ else
+ {
+ /* Start raster graphics. */
+ fz_puts(out, "\033*r1A");
+ for (; num_blank_lines; num_blank_lines--)
+ fz_puts(out, "\033*bW");
+ }
+ }
+ /* Skip blank lines if any */
+ else if (num_blank_lines != 0)
+ {
+ /* Moving down from current position causes head
+ * motion on the DeskJet, so if the number of lines
+ * is small, we're better off printing blanks.
+ *
+ * For Canon LBP4i and some others, <ESC>*b<n>Y
+ * doesn't properly clear the seed row if we are in
+ * compression mode 3.
+ */
+ if ((num_blank_lines < MIN_SKIP_LINES && compression != 3) ||
+ !(pcl->features & PCL_ANY_SPACING))
+ {
+ int mode_3ns = ((pcl->features & PCL_MODE_3_COMPRESSION) && !(pcl->features & PCL_ANY_SPACING));
+
+ if (mode_3ns && compression != 2)
+ {
+ /* Switch to mode 2 */
+ fz_puts(out, from3to2);
+ compression = 2;
+ }
+ if (pcl->features & PCL_MODE_3_COMPRESSION)
+ {
+ /* Must clear the seed row. */
+ fz_puts(out, "\033*b1Y");
+ num_blank_lines--;
+ }
+ if (mode_3ns)
+ {
+ for (; num_blank_lines; num_blank_lines--)
+ fz_puts(out, "\033*b0W");
+ }
+ else
+ {
+ for (; num_blank_lines; num_blank_lines--)
+ fz_puts(out, "\033*bW");
+ }
+ }
+ else if (pcl->features & PCL3_SPACING)
+ fz_printf(out, "\033*p+%dY", num_blank_lines * bitmap->yres);
+ else
+ fz_printf(out, "\033*b%dY", num_blank_lines);
+ /* Clear the seed row (only matters for mode 3 compression). */
+ memset(prev_row, 0, line_size);
+ }
+ num_blank_lines = 0;
+
+ /* Choose the best compression mode for this particular line. */
+ if (pcl->features & PCL_MODE_3_COMPRESSION)
+ {
+ /* Compression modes 2 and 3 are both available. Try
+ * both and see which produces the least output data.
+ */
+ int count3 = mode3compress(out_row_mode_3, data, prev_row, line_size);
+ int count2 = mode2compress(out_row_mode_2, data, line_size);
+ int penalty3 = (compression == 3 ? 0 : penalty_from2to3);
+ int penalty2 = (compression == 2 ? 0 : penalty_from3to2);
+
+ if (count3 + penalty3 < count2 + penalty2)
+ {
+ if (compression != 3)
+ fz_puts(out, from2to3);
+ compression = 3;
+ out_data = (unsigned char *)out_row_mode_3;
+ out_count = count3;
+ }
+ else
+ {
+ if (compression != 2)
+ fz_puts(out, from3to2);
+ compression = 2;
+ out_data = (unsigned char *)out_row_mode_2;
+ out_count = count2;
+ }
+ }
+ else if (pcl->features & PCL_MODE_2_COMPRESSION)
+ {
+ out_data = out_row_mode_2;
+ out_count = mode2compress(out_row_mode_2, data, line_size);
+ }
+ else
+ {
+ out_data = data;
+ out_count = line_size;
+ }
+
+ /* Transfer the data */
+ fz_printf(out, "\033*b%dW", out_count);
+ fz_write(out, out_data, out_count);
+ }
+
+ /* end raster graphics and eject page */
+ fz_puts(out, "\033*rB\f");
+
+ if (pcl->features & HACK__IS_A_OCE9050)
+ {
+ /* Pen up, pen select, advance full page, reset */
+ fz_puts(out, "\033%1BPUSP0PG;\033E");
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, prev_row);
+ fz_free(ctx, out_row_mode_2);
+ fz_free(ctx, out_row_mode_3);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+void
+fz_write_pcl(fz_context *ctx, fz_pixmap *pixmap, char *filename, int append, fz_pcl_options *pcl)
+{
+ FILE *fp;
+ fz_output *out = NULL;
+
+ fp = fopen(filename, append ? "ab" : "wb");
+ if (!fp)
+ {
+ fz_throw(ctx, "cannot open file '%s': %s", filename, strerror(errno));
+ }
+
+ fz_var(out);
+
+ fz_try(ctx)
+ {
+ out = fz_new_output_with_file(ctx, fp);
+ fz_output_pcl(out, pixmap, pcl);
+ }
+ fz_always(ctx)
+ {
+ fz_close_output(out);
+ fclose(fp);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+void
+fz_write_pcl_bitmap(fz_context *ctx, fz_bitmap *bitmap, char *filename, int append, fz_pcl_options *pcl)
+{
+ FILE *fp;
+ fz_output *out = NULL;
+
+ fp = fopen(filename, append ? "ab" : "wb");
+ if (!fp)
+ {
+ fz_throw(ctx, "cannot open file '%s': %s", filename, strerror(errno));
+ }
+
+ fz_var(out);
+
+ fz_try(ctx)
+ {
+ out = fz_new_output_with_file(ctx, fp);
+ fz_output_pcl_bitmap(out, bitmap, pcl);
+ }
+ fz_always(ctx)
+ {
+ fz_close_output(out);
+ fclose(fp);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
diff --git a/fitz/res_pixmap.c b/fitz/res_pixmap.c
index 9c346c01..e45a6e9c 100644
--- a/fitz/res_pixmap.c
+++ b/fitz/res_pixmap.c
@@ -1042,3 +1042,10 @@ fz_subsample_pixmap(fz_context *ctx, fz_pixmap *tile, int factor)
tile->h = dst_h;
tile->samples = fz_resize_array(ctx, tile->samples, dst_w * n, dst_h);
}
+
+void
+fz_pixmap_set_resolution(fz_pixmap *pix, int res)
+{
+ pix->xres = res;
+ pix->yres = res;
+}
diff --git a/win32/libmupdf.vcproj b/win32/libmupdf.vcproj
index 996b48aa..ea39bb26 100644
--- a/win32/libmupdf.vcproj
+++ b/win32/libmupdf.vcproj
@@ -557,6 +557,10 @@
>
</File>
<File
+ RelativePath="..\fitz\res_pcl.c"
+ >
+ </File>
+ <File
RelativePath="..\fitz\res_pixmap.c"
>
</File>