gdevlxm.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /* Copyright (C) 1989-1994, 1998, 1999 Aladdin Enterprises. All rights reserved.
  2. This software is provided AS-IS with no warranty, either express or
  3. implied.
  4. This software is distributed under license and may not be copied,
  5. modified or distributed except as expressly authorized under the terms
  6. of the license contained in the file LICENSE in this distribution.
  7. For more information about licensing, please refer to
  8. http://www.ghostscript.com/licensing/. For information on
  9. commercial licensing, go to http://www.artifex.com/licensing/ or
  10. contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  11. San Rafael, CA 94903, U.S.A., +1(415)492-9861.
  12. */
  13. /* $Id: gdevlxm.c,v 1.5 2004/08/10 13:02:36 stefan Exp $*/
  14. /*
  15. * Lexmark 5700 ink-jet printer driver for Ghostscript
  16. *
  17. * defines the lxm5700m device for printing in black-and-white at 1200 dpi
  18. * doesn't handle color or any other resolution.
  19. * Native resolution appears to be 600 x 1200, but print bands are overlapped.
  20. *
  21. * I use the command
  22. * gs -sOutputFile=/dev/lp0 -sDevice=lxm5700m -dHeadSeparation=15 file.ps
  23. *
  24. * where HeadSeparation varies from print-cartridge to print-cartridge and
  25. * 16 (the default) usually works fine.
  26. *
  27. * Stephen Taylor setaylor@ma.ultranet.com staylor@cs.wpi.edu
  28. */
  29. #include "gdevprn.h"
  30. #include "gsparams.h"
  31. /* The procedure descriptors */
  32. /* declare functions */
  33. private dev_proc_print_page(lxm5700m_print_page);
  34. private dev_proc_get_params(lxm_get_params);
  35. private dev_proc_put_params(lxm_put_params);
  36. /* set up dispatch table. I follow gdevdjet in using gdev_prn_output_page */
  37. static const gx_device_procs lxm5700m_procs =
  38. prn_params_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
  39. lxm_get_params, lxm_put_params);
  40. /* The device descriptors */
  41. /* define a subclass with useful state in it. */
  42. typedef struct lxm_device_s { /* a sub-class of gx_device_printer */
  43. gx_device_common;
  44. gx_prn_device_common;
  45. int headSeparation;
  46. } lxm_device;
  47. /* Standard lxm5700m device */
  48. lxm_device far_data gs_lxm5700m_device = {
  49. prn_device_std_body(lxm_device, lxm5700m_procs, "lxm5700m",
  50. DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  51. 600, 600, /* x dpi, y dpi */
  52. 0.2, 0.0, 0.0, 0.0, /* margins */
  53. 1, lxm5700m_print_page),
  54. 16 /* default headSeparation value */
  55. };
  56. /* I don't know the whole protocol for the printer, but let me tell
  57. * you about the fraction I use.
  58. * Each page begins with a header, which I describe as init1, init2, init3 --
  59. * (I separate them, because I've seen output in which those sections
  60. * seemed to appear independently, but I've deleted the only code I
  61. * had that actually used them separately.)
  62. * Then there are a number of swipe commands, each of which describes one
  63. * swipe of the printhead. Each swipe command begins with a fixed
  64. * header, which gives the number of bytes in the command,
  65. * left and right margins,
  66. * a vertical offset, some noise characters I don't know the meaning of,
  67. * and the table of bits. The bits are given one column at a time, using a
  68. * simple compression scheme: a directory, consisting of two bytes, tells
  69. * which sixteen-bit intervals of the 208-bit column contain 1-bits; then
  70. * the appropriate number of sixteen-bit bit-maps follow. (A zero-bit in the
  71. * directory indicates that a bitmap for that sector follows.) In the worst case,
  72. * this scheme would be bigger than the uncompressed bitmap, but it seems to
  73. * usually save 80-90%. The biggest complication of the bitmap scheme is this:
  74. * There are two print-heads on the black cartridge, and they are 16 pixels
  75. * (or headSeparation) apart. Odd columns of the swipe address one printhead,
  76. * and even columns the other. On the following swipe, the printheads
  77. * addressed by even and odd columns are reversed. I think the printheads might be
  78. * staggered, but the output I've seen staggers things in software;
  79. * adjacent vertical bits on the same head are not addressed; the missing
  80. * bits are written in by the second head when it passes (16 columns later.)
  81. * In my code, I call the state of addressing one head or the other "direction".
  82. * Originally I thought that the printhead was writing on both leftward and
  83. * rightward motion, and that which head was addressed by odd columns changed
  84. * accordingly. I'm no longer sure this is true, but the head addressed by the
  85. * even columns does alternate with each swipe.
  86. */
  87. /*
  88. * various output shorthands
  89. */
  90. #define init1() \
  91. top(), \
  92. 0xA5,0, 3, 0x40,4,5, \
  93. 0xA5,0, 3, 0x40,4,6, \
  94. 0xA5,0, 3, 0x40,4,7, \
  95. 0xA5,0, 3, 0x40,4,8, \
  96. 0xA5,0, 4, 0x40,0xe0,0x0b, 3
  97. #define init2() \
  98. 0xA5,0, 11, 0x40,0xe0,0x41, 0,0,0,0,0,0,0, 2, \
  99. 0xA5,0, 6, 0x40, 5, 0,0,0x80,0 \
  100. #define init3() \
  101. 0x1b,'*', 7,0x73,0x30, \
  102. 0x1b,'*', 'm', 0, 0x14, 3, 0x84, 2, 0, 1, 0xf4, \
  103. 0x1b,'*', 7,0x63, \
  104. 0x1b,'*', 'm', 0, 0x42, 0, 0, \
  105. 0xA5,0, 5, 0x40,0xe0,0x80, 8, 7, \
  106. 0x1b,'*', 'm', 0, 0x40, 0x15, 7, 0x0f, 0x0f \
  107. #define top() \
  108. 0xA5,0, 6, 0x40, 3,3,0xc0,0x0f,0x0f \
  109. #define fin() \
  110. 0x1b,'*', 7, 0x65 \
  111. #define outByte(b) putc(b, prn_stream)
  112. #define RIGHTWARD 0
  113. #define LEFTWARD 1
  114. /* number of pixels between even columns in output and odd ones*/
  115. /* #define headSeparation 16 */
  116. /* overlap between successive swipes of the print head */
  117. #define overLap 104
  118. /* height of printhead in pixels */
  119. #define swipeHeight 208
  120. /* number of shorts described by each column directory */
  121. #define directorySize 13
  122. /* ------ Driver procedures ------ */
  123. /* Send the page to the printer. */
  124. private int
  125. lxm5700m_print_page(gx_device_printer *pdev, FILE *prn_stream)
  126. {
  127. int lnum,minX, maxX, i, l, highestX, leastX, extent;
  128. int direction = RIGHTWARD;
  129. int lastY = 0;
  130. int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
  131. /* Note that in_size is a multiple of 8. */
  132. int in_size = line_size * (swipeHeight);
  133. int swipeBuf_size = in_size;
  134. byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "lxm_print_page(buf1)");
  135. byte *swipeBuf =
  136. (byte *)gs_malloc(pdev->memory, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
  137. byte *in = buf1;
  138. /* Check allocations */
  139. if ( buf1 == 0 || swipeBuf == 0 ) {
  140. if ( buf1 )
  141. quit_ignomiously: /* and a goto into an if statement is pretty ignomious! */
  142. gs_free(pdev->memory, (char *)buf1, in_size, 1, "lxm_print_page(buf1)");
  143. if ( swipeBuf )
  144. gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
  145. return_error(gs_error_VMerror);
  146. }
  147. { /* Initialize the printer and reset the margins. */
  148. static const char init_string[] = {
  149. init1(),
  150. init2(),
  151. init3()
  152. };
  153. fwrite(init_string, 1, sizeof(init_string), prn_stream);
  154. }
  155. /* Print lines of graphics */
  156. for (lnum=0; lnum < pdev->height-swipeHeight ; ) { /* increment in body */
  157. byte *in_data;
  158. register byte *outp;
  159. int lcnt;
  160. { /* test for blank scan lines. We maintain the */
  161. /* loop invariant lnum <pdev->height, but modify lnum */
  162. int l;
  163. for (l=lnum; l<pdev->height; l++) {
  164. /* Copy 1 scan line and test for all zero. */
  165. gdev_prn_get_bits(pdev, l, in, &in_data);
  166. if ( in_data[0] != 0 ||
  167. memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
  168. ) {
  169. break;
  170. }
  171. }/* end for l */
  172. /* now l is the next non-blank scan line */
  173. if (l >= pdev->height) {/* if there are no more bits on this page */
  174. lnum = l;
  175. break; /* end the loop and eject the page*/
  176. }
  177. /* leave room for following swipe to reinforce these bits */
  178. if (l-lnum > overLap) lnum = l - overLap;
  179. /* if the first non-blank near bottom of page */
  180. if (lnum >=pdev->height - swipeHeight) {
  181. /* don't move the printhead over empty air*/
  182. lnum = pdev->height - swipeHeight;
  183. }
  184. }
  185. /* Copy the the scan lines. */
  186. lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
  187. if ( lcnt < swipeHeight ) {
  188. /* Pad with lines of zeros. */
  189. memset(in + lcnt * line_size, 0,
  190. in_size - lcnt * line_size);
  191. }
  192. /* compute right and left margin for this swipe */
  193. minX = line_size;
  194. maxX = 0;
  195. for (l=0; l<swipeHeight; l++) {/* for each line of swipe */
  196. for (i=0; i<minX; i++) {/* look for left-most non-zero byte*/
  197. if (in[l*line_size+i] !=0) {
  198. minX = i;
  199. break;
  200. }
  201. }
  202. for (i=line_size-1; i>=maxX; i--) {/* look for right-most */
  203. if (in[l*line_size+i] !=0) {
  204. maxX = i;
  205. break;
  206. }
  207. }
  208. }
  209. minX = (minX&(-2)); /* truncate to even */
  210. maxX = (maxX+3)&-2; /* raise to even */
  211. highestX = maxX*8-1;
  212. leastX = minX*8;
  213. extent = highestX -leastX +1;
  214. outp = swipeBuf;
  215. /* macro, not fcn call. Space penalty is modest, speed helps */
  216. #define buffer_store(x) if(outp-swipeBuf>=swipeBuf_size) {\
  217. gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
  218. swipeBuf_size*=2;\
  219. swipeBuf = (byte *)gs_malloc(pdev->memory, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
  220. if (swipeBuf == 0) goto quit_ignomiously;\
  221. break;}\
  222. else *outp++ = (x)
  223. {/* work out the bytes to store for this swipe*/
  224. int sx, sxBy8, sxMask;
  225. int words[directorySize];
  226. bool f, sum;
  227. int retval=0;
  228. int j,c,y;
  229. int j1,c1;
  230. int i,b,x, directory ;
  231. /* want to set up pointers for (upto two) stripes covered by the output*/
  232. /* now for each column covered by output: */
  233. for (x=leastX; x<=highestX; x++) {
  234. for (i=0; i<directorySize; i++) {
  235. words[i] = 0;
  236. }
  237. directory = 0x2000; /* empty directory != 0 */
  238. /* prime loops: make comparisons here */
  239. switch (direction) {
  240. case(RIGHTWARD):
  241. sx = (x&1)==1 ? x : x-(((lxm_device*)pdev)->headSeparation);
  242. j1 = (x&1); /* even if x even, odd if x odd */
  243. break;
  244. default: /* shouldn't happen ... but compilation checks */
  245. case(LEFTWARD):
  246. sx = (x&1)==0 ? x : x-((lxm_device*)pdev)->headSeparation;
  247. j1 = 1-(x&1); /* odd if x even, even if x odd */
  248. }
  249. c1 = 0x8000 >> j1;
  250. sxBy8 = sx/8;
  251. sxMask = 0x80>>(sx%8);
  252. /* loop through all the swipeHeight bits of this column */
  253. for (i = 0, b=1, y= sxBy8+j1*line_size; i < directorySize; i++,b<<=1) {
  254. sum = false;
  255. for (j=j1,c=c1 /*,y=i*16*line_size+sxBy8*/; j<16; j+=2, y+=2*line_size, c>>=2) {
  256. f = (in[y]&sxMask);
  257. if (f) {
  258. words[i] |= c;
  259. sum |= f;
  260. }
  261. }
  262. if (!sum) directory |=b;
  263. }
  264. retval+=2;
  265. buffer_store(directory>>8); buffer_store(directory&0xff);
  266. if (directory != 0x3fff) {
  267. for (i=0; i<directorySize; i++) {
  268. if (words[i] !=0) {
  269. buffer_store(words[i]>>8) ; buffer_store(words[i]&0xff);
  270. retval += 2;
  271. }
  272. }
  273. }
  274. }
  275. #undef buffer_store
  276. }
  277. {/* now write out header, then buffered bits */
  278. int leastY = lnum;
  279. /* compute size of swipe, needed for header */
  280. int sz = 0x1a + outp - swipeBuf;
  281. /* put out header*/
  282. int deltaY = 2*(leastY - lastY); /* vert coordinates here are 1200 dpi */
  283. lastY = leastY;
  284. outByte(0x1b); outByte('*'); outByte(3);
  285. outByte(deltaY>>8); outByte(deltaY&0xff);
  286. outByte(0x1b); outByte('*'); outByte(4); outByte(0); outByte(0);
  287. outByte(sz>>8); outByte(sz&0xff); outByte(0); outByte(3);
  288. outByte(1); outByte(1); outByte(0x1a);
  289. outByte(0);
  290. outByte(extent>>8); outByte(extent&0xff);
  291. outByte(leastX>>8); outByte(leastX&0xff);
  292. outByte(highestX>>8); outByte(highestX&0xff);
  293. outByte(0); outByte(0);
  294. outByte(0x22); outByte(0x33); outByte(0x44);
  295. outByte(0x55); outByte(1);
  296. /* put out bytes */
  297. fwrite(swipeBuf,1,outp-swipeBuf,prn_stream);
  298. }
  299. lnum += overLap;
  300. direction ^= 1;
  301. }/* ends the loop for swipes of the print head.*/
  302. /* Eject the page and reinitialize the printer */
  303. {
  304. static const char bottom[] = {
  305. fin() /*, looks like I can get away with only this much ...
  306. init1(),
  307. init3(),
  308. fin() ,
  309. top(),
  310. fin() */
  311. };
  312. fwrite(bottom, 1, sizeof(bottom), prn_stream);
  313. }
  314. fflush(prn_stream);
  315. gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
  316. gs_free(pdev->memory, (char *)buf1, in_size, 1, "lxm_print_page(buf1)");
  317. return 0;
  318. }
  319. /*
  320. * There are a number of parameters which can differ between ink cartridges.
  321. * The Windows driver asks you to recalibrate every time you load a new
  322. * cartridge.
  323. * most of the parameters adjusted there relate to color, and so this
  324. * monotone driver doesn't need them. However, the Lexmark 5700 black
  325. * cartridge has two columns of dots, separated by about 16 pixels.
  326. * This `head separation' distance can vary between cartridges, so
  327. * we provide a parameter to set it. In my small experience I've not
  328. * set the corresponding parameter in windows to anything greater than 17
  329. * or smaller than 15, but it would seem that it can vary from 1 to 32,
  330. * based on the calibration choices offered.
  331. *
  332. * As I understand the rules laid out in gsparams.h,
  333. * lxm_get_params is supposed to return the current values of parameters
  334. * and lxm_put_params is supposed to set up values in the lxm_device
  335. * structure which can be used by the lxm5700m_print_page routine.
  336. * I've copied my routines from gdevcdj.c
  337. */
  338. private int
  339. lxm_get_params(gx_device *pdev, gs_param_list *plist)
  340. {
  341. lxm_device* const ldev = (lxm_device*)pdev;
  342. int code = gdev_prn_get_params(pdev, plist);
  343. if ( code < 0 ) return code;
  344. code = param_write_int(plist,
  345. "HeadSeparation",
  346. (int *)&(ldev->headSeparation));
  347. return code;
  348. }
  349. /* put_params is supposed to check all the parameters before setting any. */
  350. private int
  351. lxm_put_params(gx_device *pdev, gs_param_list *plist)
  352. {
  353. int ecode;
  354. lxm_device* const ldev = (lxm_device*)pdev;
  355. int trialHeadSeparation=ldev->headSeparation;
  356. int code = param_read_int(plist, "HeadSeparation", &trialHeadSeparation);
  357. if ( trialHeadSeparation < 1 || trialHeadSeparation > 32 )
  358. param_signal_error(plist, "HeadSeparation", gs_error_rangecheck);
  359. /* looks like param_signal_error is not expected to return */
  360. ecode = gdev_prn_put_params(pdev, plist); /* call super class put_params */
  361. if ( code < 0 ) return code;
  362. if (ecode < 0) return ecode;
  363. /* looks like everything okay; go ahead and set headSeparation */
  364. ldev->headSeparation = trialHeadSeparation;
  365. if ( code == 1) return ecode; /* I guess this means there is no "HeadSeparation" parameter */
  366. return 0;
  367. }