13ff48bf5SDavid du Colombier /* Copyright (C) 2000 Aladdin Enterprises. All rights reserved.
23ff48bf5SDavid du Colombier
3*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier implied.
53ff48bf5SDavid du Colombier
6*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
93ff48bf5SDavid du Colombier
10*593dc095SDavid du Colombier For more information about licensing, please refer to
11*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
153ff48bf5SDavid du Colombier */
163ff48bf5SDavid du Colombier
17*593dc095SDavid du Colombier /* $Id: gdevatx.c,v 1.9 2004/01/29 18:19:41 ray Exp $ */
183ff48bf5SDavid du Colombier /* Practical Automation ATX-23, -24, and -38 driver */
193ff48bf5SDavid du Colombier #include "math_.h"
203ff48bf5SDavid du Colombier #include "gdevprn.h"
213ff48bf5SDavid du Colombier
223ff48bf5SDavid du Colombier /*
233ff48bf5SDavid du Colombier * All of the ATX printers have an unprintable margin of 0.125" at the top
243ff48bf5SDavid du Colombier * and bottom of the page. They also have unprintable left/right margins:
253ff48bf5SDavid du Colombier * ATX-23 0.25"
263ff48bf5SDavid du Colombier * ATX-24 0.193"
273ff48bf5SDavid du Colombier * ATS-38 0.25"
283ff48bf5SDavid du Colombier * The code below assumes that coordinates refer only to the *printable*
293ff48bf5SDavid du Colombier * part of each page. This is wrong and must eventually be changed.
303ff48bf5SDavid du Colombier */
313ff48bf5SDavid du Colombier
323ff48bf5SDavid du Colombier /* Define the printer commands. */
333ff48bf5SDavid du Colombier #define ATX_SET_PAGE_LENGTH "\033f" /* + 2-byte length */
343ff48bf5SDavid du Colombier #define ATX_VERTICAL_TAB "\033L" /* + 2-byte count */
353ff48bf5SDavid du Colombier #define ATX_UNCOMPRESSED_DATA "\033d" /* + 2-byte count */
363ff48bf5SDavid du Colombier #define ATX_COMPRESSED_DATA "\033x" /* + 1-byte word count */
373ff48bf5SDavid du Colombier #define ATX_END_PAGE "\033e"
383ff48bf5SDavid du Colombier
393ff48bf5SDavid du Colombier /* The device descriptors */
403ff48bf5SDavid du Colombier private dev_proc_print_page(atx23_print_page);
413ff48bf5SDavid du Colombier private dev_proc_print_page(atx24_print_page);
423ff48bf5SDavid du Colombier private dev_proc_print_page(atx38_print_page);
433ff48bf5SDavid du Colombier
443ff48bf5SDavid du Colombier #define ATX_DEVICE(dname, w10, h10, dpi, lrm, btm, print_page)\
453ff48bf5SDavid du Colombier prn_device_margins(prn_std_procs, dname, w10, h10, dpi, dpi, 0, 0,\
463ff48bf5SDavid du Colombier lrm, btm, lrm, btm, 1, print_page)
473ff48bf5SDavid du Colombier
483ff48bf5SDavid du Colombier const gx_device_printer gs_atx23_device = /* real width = 576 pixels */
493ff48bf5SDavid du Colombier ATX_DEVICE("atx23", 28 /* 2.84" */, 35 /* (minimum) */,
503ff48bf5SDavid du Colombier 203, 0.25, 0.125, atx23_print_page);
513ff48bf5SDavid du Colombier
523ff48bf5SDavid du Colombier const gx_device_printer gs_atx24_device = /* real width = 832 pixels */
533ff48bf5SDavid du Colombier ATX_DEVICE("atx24", 41 /* 4.1" */, 35 /* (minimum) */,
543ff48bf5SDavid du Colombier 203, 0.193, 0.125, atx24_print_page);
553ff48bf5SDavid du Colombier
563ff48bf5SDavid du Colombier const gx_device_printer gs_atx38_device = /* real width = 2400 pixels */
573ff48bf5SDavid du Colombier ATX_DEVICE("atx38", 80 /* 8.0" */, 35 /* (minimum) */,
583ff48bf5SDavid du Colombier 300, 0.25, 0.125, atx38_print_page);
593ff48bf5SDavid du Colombier
603ff48bf5SDavid du Colombier /* Output a printer command with a 2-byte, little-endian numeric argument. */
613ff48bf5SDavid du Colombier private void
fput_atx_command(FILE * f,const char * str,int value)623ff48bf5SDavid du Colombier fput_atx_command(FILE *f, const char *str, int value)
633ff48bf5SDavid du Colombier {
643ff48bf5SDavid du Colombier fputs(str, f);
653ff48bf5SDavid du Colombier fputc((byte)value, f);
663ff48bf5SDavid du Colombier fputc((byte)(value >> 8), f);
673ff48bf5SDavid du Colombier }
683ff48bf5SDavid du Colombier
693ff48bf5SDavid du Colombier /*
703ff48bf5SDavid du Colombier * Attempt to compress a scan line of data. in_size and out_size are even.
713ff48bf5SDavid du Colombier * Return -1 if the compressed data would exceed out_size, otherwise the
723ff48bf5SDavid du Colombier * size of the compressed data (always even).
733ff48bf5SDavid du Colombier */
743ff48bf5SDavid du Colombier #define MIN_IN_SIZE_TO_COMPRESS 50
753ff48bf5SDavid du Colombier #define MAX_COMPRESSED_SEGMENT_PAIRS 127
763ff48bf5SDavid du Colombier #define MAX_UNCOMPRESSED_SEGMENT_PAIRS 255
773ff48bf5SDavid du Colombier #define COMPRESSED_SEGMENT_COMMAND 0x80 /* + # of repeated pairs */
783ff48bf5SDavid du Colombier #define UNCOMPRESSED_SEGMENT_COMMAND 0x7f /* followed by # of pairs */
793ff48bf5SDavid du Colombier private int
atx_compress(const byte * in_buf,int in_size,byte * out_buf,int out_size)803ff48bf5SDavid du Colombier atx_compress(const byte *in_buf, int in_size, byte *out_buf, int out_size)
813ff48bf5SDavid du Colombier {
823ff48bf5SDavid du Colombier const byte *const in_end = in_buf + in_size;
833ff48bf5SDavid du Colombier byte *const out_end = out_buf + out_size;
843ff48bf5SDavid du Colombier const byte *in = in_buf;
853ff48bf5SDavid du Colombier byte *out = out_buf;
863ff48bf5SDavid du Colombier byte *out_command;
873ff48bf5SDavid du Colombier int pair_count;
883ff48bf5SDavid du Colombier
893ff48bf5SDavid du Colombier if (in_size < MIN_IN_SIZE_TO_COMPRESS)
903ff48bf5SDavid du Colombier return -1; /* not worth compressing */
913ff48bf5SDavid du Colombier
923ff48bf5SDavid du Colombier /* Start a new segment. */
933ff48bf5SDavid du Colombier New_Segment:
943ff48bf5SDavid du Colombier if (in == in_end) /* end of input data */
953ff48bf5SDavid du Colombier return out - out_buf;
963ff48bf5SDavid du Colombier if (out == out_end) /* output buffer full */
973ff48bf5SDavid du Colombier return -1;
983ff48bf5SDavid du Colombier out_command = out;
993ff48bf5SDavid du Colombier out += 2;
1003ff48bf5SDavid du Colombier if (in[1] == in[0]) { /* start compressed segment */
1013ff48bf5SDavid du Colombier /* out[-2] will be compressed segment command */
1023ff48bf5SDavid du Colombier out[-1] = in[0];
1033ff48bf5SDavid du Colombier pair_count = 1;
1043ff48bf5SDavid du Colombier goto Scan_Compressed_Pair;
1053ff48bf5SDavid du Colombier } else { /* start uncompressed segment */
1063ff48bf5SDavid du Colombier out[-2] = UNCOMPRESSED_SEGMENT_COMMAND;
1073ff48bf5SDavid du Colombier /* out[-1] will be pair count */
1083ff48bf5SDavid du Colombier pair_count = 0;
1093ff48bf5SDavid du Colombier goto Scan_Uncompressed_Pair;
1103ff48bf5SDavid du Colombier }
1113ff48bf5SDavid du Colombier
1123ff48bf5SDavid du Colombier /* Scan compressed data. */
1133ff48bf5SDavid du Colombier Scan_Compressed:
1143ff48bf5SDavid du Colombier if (pair_count == MAX_COMPRESSED_SEGMENT_PAIRS ||
1153ff48bf5SDavid du Colombier in == in_end || in[0] != in[-1] || in[1] != in[0]
1163ff48bf5SDavid du Colombier ) { /* end the segment */
1173ff48bf5SDavid du Colombier out_command[0] = COMPRESSED_SEGMENT_COMMAND + pair_count;
1183ff48bf5SDavid du Colombier goto New_Segment;
1193ff48bf5SDavid du Colombier }
1203ff48bf5SDavid du Colombier ++pair_count;
1213ff48bf5SDavid du Colombier Scan_Compressed_Pair:
1223ff48bf5SDavid du Colombier in += 2;
1233ff48bf5SDavid du Colombier goto Scan_Compressed;
1243ff48bf5SDavid du Colombier
1253ff48bf5SDavid du Colombier /* Scan uncompressed data. */
1263ff48bf5SDavid du Colombier Scan_Uncompressed:
1273ff48bf5SDavid du Colombier if (pair_count == MAX_UNCOMPRESSED_SEGMENT_PAIRS ||
1283ff48bf5SDavid du Colombier in == in_end || in[1] == in[0]
1293ff48bf5SDavid du Colombier ) { /* end the segment */
1303ff48bf5SDavid du Colombier out_command[1] = pair_count;
1313ff48bf5SDavid du Colombier goto New_Segment;
1323ff48bf5SDavid du Colombier }
1333ff48bf5SDavid du Colombier Scan_Uncompressed_Pair:
1343ff48bf5SDavid du Colombier if (out == out_end) /* output buffer full */
1353ff48bf5SDavid du Colombier return -1;
1363ff48bf5SDavid du Colombier out[0] = in[0], out[1] = in[1];
1373ff48bf5SDavid du Colombier in += 2;
1383ff48bf5SDavid du Colombier out += 2;
1393ff48bf5SDavid du Colombier ++pair_count;
1403ff48bf5SDavid du Colombier goto Scan_Uncompressed;
1413ff48bf5SDavid du Colombier
1423ff48bf5SDavid du Colombier }
1433ff48bf5SDavid du Colombier
1443ff48bf5SDavid du Colombier /* Send the page to the printer. */
1453ff48bf5SDavid du Colombier private int
atx_print_page(gx_device_printer * pdev,FILE * f,int max_width_bytes)1463ff48bf5SDavid du Colombier atx_print_page(gx_device_printer *pdev, FILE *f, int max_width_bytes)
1473ff48bf5SDavid du Colombier {
1483ff48bf5SDavid du Colombier /*
1493ff48bf5SDavid du Colombier * The page length command uses 16 bits to represent the length in
1503ff48bf5SDavid du Colombier * units of 0.01", so the maximum representable page length is
1513ff48bf5SDavid du Colombier * 655.35", including the unprintable top and bottom margins.
1523ff48bf5SDavid du Colombier * Compute the maximum height of the printable area in pixels.
1533ff48bf5SDavid du Colombier */
1543ff48bf5SDavid du Colombier float top_bottom_skip = (pdev->HWMargins[1] + pdev->HWMargins[3]) / 72.0;
1553ff48bf5SDavid du Colombier int max_height = (int)(pdev->HWResolution[1] * 655 - top_bottom_skip);
1563ff48bf5SDavid du Colombier int height = min(pdev->height, max_height);
1573ff48bf5SDavid du Colombier int page_length_100ths =
1583ff48bf5SDavid du Colombier (int)ceil((height / pdev->HWResolution[1] + top_bottom_skip) * 100);
1593ff48bf5SDavid du Colombier gs_memory_t *mem = pdev->memory;
1603ff48bf5SDavid du Colombier int raster = gx_device_raster((gx_device *)pdev, true);
1613ff48bf5SDavid du Colombier byte *buf;
1623ff48bf5SDavid du Colombier /*
1633ff48bf5SDavid du Colombier * ATX_COMPRESSED_DATA only takes a 1-byte (word) count.
1643ff48bf5SDavid du Colombier * Thus no compressed scan line can take more than 510 bytes.
1653ff48bf5SDavid du Colombier */
1663ff48bf5SDavid du Colombier int compressed_raster = min(raster / 2, 510); /* require 50% compression */
1673ff48bf5SDavid du Colombier byte *compressed;
1683ff48bf5SDavid du Colombier int blank_lines, lnum;
1693ff48bf5SDavid du Colombier int code = 0;
1703ff48bf5SDavid du Colombier
1713ff48bf5SDavid du Colombier /* Enforce a minimum 3" page length. */
1723ff48bf5SDavid du Colombier if (page_length_100ths < 300)
1733ff48bf5SDavid du Colombier page_length_100ths = 300;
1743ff48bf5SDavid du Colombier buf = gs_alloc_bytes(mem, raster, "atx_print_page(buf)");
1753ff48bf5SDavid du Colombier compressed = gs_alloc_bytes(mem, compressed_raster,
1763ff48bf5SDavid du Colombier "atx_print_page(compressed)");
1773ff48bf5SDavid du Colombier if (buf == 0 || compressed == 0) {
1783ff48bf5SDavid du Colombier code = gs_note_error(gs_error_VMerror);
1793ff48bf5SDavid du Colombier goto done;
1803ff48bf5SDavid du Colombier }
1813ff48bf5SDavid du Colombier fput_atx_command(f, ATX_SET_PAGE_LENGTH, page_length_100ths);
1823ff48bf5SDavid du Colombier for (blank_lines = 0, lnum = 0; lnum < height; ++lnum) {
1833ff48bf5SDavid du Colombier byte *row;
1843ff48bf5SDavid du Colombier byte *end;
1853ff48bf5SDavid du Colombier int count;
1863ff48bf5SDavid du Colombier
1873ff48bf5SDavid du Colombier gdev_prn_get_bits(pdev, lnum, buf, &row);
1883ff48bf5SDavid du Colombier /* Find the end of the non-blank data. */
1893ff48bf5SDavid du Colombier for (end = row + raster; end > row && end[-1] == 0 && end[-2] == 0; )
1903ff48bf5SDavid du Colombier end -= 2;
1913ff48bf5SDavid du Colombier if (end == row) { /* blank line */
1923ff48bf5SDavid du Colombier ++blank_lines;
1933ff48bf5SDavid du Colombier continue;
1943ff48bf5SDavid du Colombier }
1953ff48bf5SDavid du Colombier if (blank_lines) { /* skip vertically */
1963ff48bf5SDavid du Colombier fput_atx_command(f, ATX_VERTICAL_TAB, blank_lines + 1);
1973ff48bf5SDavid du Colombier blank_lines = 0;
1983ff48bf5SDavid du Colombier }
1993ff48bf5SDavid du Colombier /* Truncate the line to the maximum printable width. */
2003ff48bf5SDavid du Colombier if (end - row > max_width_bytes)
2013ff48bf5SDavid du Colombier end = row + max_width_bytes;
2023ff48bf5SDavid du Colombier count = atx_compress(row, end - row, compressed, compressed_raster);
2033ff48bf5SDavid du Colombier if (count >= 0) { /* compressed line */
2043ff48bf5SDavid du Colombier /*
2053ff48bf5SDavid du Colombier * Note that since compressed_raster can't exceed 510, count
2063ff48bf5SDavid du Colombier * can't exceed 510 either.
2073ff48bf5SDavid du Colombier */
2083ff48bf5SDavid du Colombier fputs(ATX_COMPRESSED_DATA, f);
2093ff48bf5SDavid du Colombier fputc(count / 2, f);
2103ff48bf5SDavid du Colombier fwrite(compressed, 1, count, f);
2113ff48bf5SDavid du Colombier } else { /* uncompressed line */
2123ff48bf5SDavid du Colombier int num_bytes = end - row;
2133ff48bf5SDavid du Colombier
2143ff48bf5SDavid du Colombier fput_atx_command(f, ATX_UNCOMPRESSED_DATA, num_bytes);
2153ff48bf5SDavid du Colombier fwrite(row, 1, num_bytes, f);
2163ff48bf5SDavid du Colombier }
2173ff48bf5SDavid du Colombier }
2183ff48bf5SDavid du Colombier
2193ff48bf5SDavid du Colombier #if 0 /**************** MAY NOT BE NEEDED ****************/
2203ff48bf5SDavid du Colombier /* Enforce the minimum page length, and skip any final blank lines. */
2213ff48bf5SDavid du Colombier {
2223ff48bf5SDavid du Colombier int paper_length = (int)(pdev->HWResolution[1] * 3 + 0.5);
2233ff48bf5SDavid du Colombier int printed_length = height - blank_lines;
2243ff48bf5SDavid du Colombier
2253ff48bf5SDavid du Colombier if (height > paper_length)
2263ff48bf5SDavid du Colombier paper_length = height;
2273ff48bf5SDavid du Colombier if (printed_length < paper_length)
2283ff48bf5SDavid du Colombier fput_atx_command(f, ATX_VERTICAL_TAB,
2293ff48bf5SDavid du Colombier paper_length - printed_length + 1);
2303ff48bf5SDavid du Colombier }
2313ff48bf5SDavid du Colombier #endif
2323ff48bf5SDavid du Colombier
2333ff48bf5SDavid du Colombier /* End the page. */
2343ff48bf5SDavid du Colombier fputs(ATX_END_PAGE, f);
2353ff48bf5SDavid du Colombier
2363ff48bf5SDavid du Colombier done:
2373ff48bf5SDavid du Colombier gs_free_object(mem, compressed, "atx_print_page(compressed)");
2383ff48bf5SDavid du Colombier gs_free_object(mem, buf, "atx_print_page(buf)");
2393ff48bf5SDavid du Colombier return code;
2403ff48bf5SDavid du Colombier }
2413ff48bf5SDavid du Colombier
2423ff48bf5SDavid du Colombier /* Print pages with specified maximum pixel widths. */
2433ff48bf5SDavid du Colombier private int
atx23_print_page(gx_device_printer * pdev,FILE * f)2443ff48bf5SDavid du Colombier atx23_print_page(gx_device_printer *pdev, FILE *f)
2453ff48bf5SDavid du Colombier {
2463ff48bf5SDavid du Colombier return atx_print_page(pdev, f, 576 / 8);
2473ff48bf5SDavid du Colombier }
2483ff48bf5SDavid du Colombier private int
atx24_print_page(gx_device_printer * pdev,FILE * f)2493ff48bf5SDavid du Colombier atx24_print_page(gx_device_printer *pdev, FILE *f)
2503ff48bf5SDavid du Colombier {
2513ff48bf5SDavid du Colombier return atx_print_page(pdev, f, 832 / 8);
2523ff48bf5SDavid du Colombier }
2533ff48bf5SDavid du Colombier private int
atx38_print_page(gx_device_printer * pdev,FILE * f)2543ff48bf5SDavid du Colombier atx38_print_page(gx_device_printer *pdev, FILE *f)
2553ff48bf5SDavid du Colombier {
2563ff48bf5SDavid du Colombier return atx_print_page(pdev, f, 2400 / 8);
2573ff48bf5SDavid du Colombier }
258