1/*
2 * "$Id: escp2-driver.c,v 1.57 2010/12/19 02:51:37 rlk Exp $"
3 *
4 *   Print plug-in EPSON ESC/P2 driver for the GIMP.
5 *
6 *   Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
7 *	Robert Krawitz (rlk@alum.mit.edu)
8 *
9 *   This program is free software; you can redistribute it and/or modify it
10 *   under the terms of the GNU General Public License as published by the Free
11 *   Software Foundation; either version 2 of the License, or (at your option)
12 *   any later version.
13 *
14 *   This program is distributed in the hope that it will be useful, but
15 *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 *   for more details.
18 *
19 *   You should have received a copy of the GNU General Public License
20 *   along with this program; if not, write to the Free Software
21 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24/*
25 * This file must include only standard C header files.  The core code must
26 * compile on generic platforms that don't support glib, gimp, gtk, etc.
27 */
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include <gutenprint/gutenprint.h>
33#include <gutenprint/gutenprint-intl-internal.h>
34#include "gutenprint-internal.h"
35#include <string.h>
36#include "print-escp2.h"
37
38#ifdef __GNUC__
39#define inline __inline__
40#endif
41
42static escp2_privdata_t *
43get_privdata(stp_vars_t *v)
44{
45  return (escp2_privdata_t *) stp_get_component_data(v, "Driver");
46}
47
48static void
49escp2_reset_printer(stp_vars_t *v)
50{
51  escp2_privdata_t *pd = get_privdata(v);
52  /*
53   * Magic initialization string that's needed to take printer out of
54   * packet mode.
55   */
56  if (pd->preinit_sequence)
57    stp_write_raw(pd->preinit_sequence, v);
58
59  stp_send_command(v, "\033@", "");
60}
61
62static void
63print_remote_param(stp_vars_t *v, const char *param, const char *value)
64{
65  stp_send_command(v, "\033(R", "bcscs", '\0', param, ':',
66		    value ? value : "NULL");
67  stp_send_command(v, "\033", "ccc", 0, 0, 0);
68}
69
70static void
71print_remote_int_param(stp_vars_t *v, const char *param, int value)
72{
73  char buf[64];
74  (void) snprintf(buf, 64, "%d", value);
75  print_remote_param(v, param, buf);
76}
77
78static void
79print_remote_float_param(stp_vars_t *v, const char *param, double value)
80{
81  char buf[64];
82  (void) snprintf(buf, 64, "%f", value);
83  print_remote_param(v, param, buf);
84}
85
86static void
87print_debug_params(stp_vars_t *v)
88{
89  escp2_privdata_t *pd = get_privdata(v);
90  stp_parameter_list_t params = stp_get_parameter_list(v);
91  int count = stp_parameter_list_count(params);
92  int i;
93  print_remote_param(v, "Package", PACKAGE);
94  print_remote_param(v, "Version", VERSION);
95  print_remote_param(v, "Release Date", RELEASE_DATE);
96  print_remote_param(v, "Driver", stp_get_driver(v));
97  print_remote_int_param(v, "Left", stp_get_left(v));
98  print_remote_int_param(v, "Top", stp_get_top(v));
99  print_remote_int_param(v, "Page Width", stp_get_page_width(v));
100  print_remote_int_param(v, "Page Height", stp_get_page_height(v));
101  print_remote_int_param(v, "Model", stp_get_model_id(v));
102  print_remote_int_param(v, "Ydpi", pd->res->vres);
103  print_remote_int_param(v, "Xdpi", pd->res->hres);
104  print_remote_int_param(v, "Printed_ydpi", pd->res->printed_vres);
105  print_remote_int_param(v, "Printed_xdpi", pd->res->printed_hres);
106/*
107  print_remote_int_param(v, "Use_softweave", pd->res->softweave);
108  print_remote_int_param(v, "Printer_weave", pd->res->printer_weave);
109*/
110  print_remote_int_param(v, "Use_printer_weave", pd->use_printer_weave);
111  print_remote_int_param(v, "Duplex", pd->duplex);
112  print_remote_int_param(v, "Page_left", pd->page_left);
113  print_remote_int_param(v, "Page_right", pd->page_right);
114  print_remote_int_param(v, "Page_top", pd->page_top);
115  print_remote_int_param(v, "Page_bottom", pd->page_bottom);
116  print_remote_int_param(v, "Page_width", pd->page_width);
117  print_remote_int_param(v, "Page_height", pd->page_height);
118  print_remote_int_param(v, "Page_true_height", pd->page_true_height);
119  print_remote_int_param(v, "Page_extra_height", pd->page_extra_height);
120  print_remote_int_param(v, "Paper_extra_bottom", pd->paper_extra_bottom);
121  print_remote_int_param(v, "Image_left", pd->image_left);
122  print_remote_int_param(v, "Image_top", pd->image_top);
123  print_remote_int_param(v, "Image_width", pd->image_width);
124  print_remote_int_param(v, "Image_height", pd->image_height);
125  print_remote_int_param(v, "Image_scaled_width", pd->image_scaled_width);
126  print_remote_int_param(v, "Image_scaled_height", pd->image_scaled_height);
127  print_remote_int_param(v, "Image_printed_width", pd->image_printed_width);
128  print_remote_int_param(v, "Image_printed_height", pd->image_printed_height);
129  print_remote_int_param(v, "Image_left_position", pd->image_left_position);
130  print_remote_int_param(v, "Nozzles", pd->nozzles);
131  print_remote_int_param(v, "Nozzle_separation", pd->nozzle_separation);
132  print_remote_int_param(v, "Horizontal_passes", pd->horizontal_passes);
133  print_remote_int_param(v, "Vertical_passes", pd->res->vertical_passes);
134  print_remote_int_param(v, "Physical_xdpi", pd->physical_xdpi);
135  print_remote_int_param(v, "Page_management_units", pd->page_management_units);
136  print_remote_int_param(v, "Vertical_units", pd->vertical_units);
137  print_remote_int_param(v, "Horizontal_units", pd->horizontal_units);
138  print_remote_int_param(v, "Micro_units", pd->micro_units);
139  print_remote_int_param(v, "Unit_scale", pd->unit_scale);
140  print_remote_int_param(v, "Zero_advance", pd->send_zero_pass_advance);
141  print_remote_int_param(v, "Bits", pd->bitwidth);
142  print_remote_int_param(v, "Drop Size", pd->drop_size);
143  print_remote_int_param(v, "Initial_vertical_offset", pd->initial_vertical_offset);
144  print_remote_int_param(v, "Channels_in_use", pd->channels_in_use);
145  print_remote_int_param(v, "Logical_channels", pd->logical_channels);
146  print_remote_int_param(v, "Physical_channels", pd->physical_channels);
147  print_remote_int_param(v, "Use_black_parameters", pd->use_black_parameters);
148  print_remote_int_param(v, "Use_fast_360", pd->use_fast_360);
149  print_remote_int_param(v, "Command_set", pd->command_set);
150  print_remote_int_param(v, "Variable_dots", pd->variable_dots);
151  print_remote_int_param(v, "Has_graymode", pd->has_graymode);
152  print_remote_int_param(v, "Base_separation", pd->base_separation);
153  print_remote_int_param(v, "Resolution_scale", pd->resolution_scale);
154#if 0
155  print_remote_int_param(v, "Printing_resolution", pd->printing_resolution);
156#endif
157  print_remote_int_param(v, "Separation_rows", pd->separation_rows);
158  print_remote_int_param(v, "Pseudo_separation_rows", pd->pseudo_separation_rows);
159  print_remote_int_param(v, "Extra_720dpi_separation", pd->extra_720dpi_separation);
160  print_remote_int_param(v, "Use_aux_channels", pd->use_aux_channels);
161  print_remote_param(v, "Ink name", pd->inkname->name);
162  print_remote_int_param(v, "  channels", pd->inkname->channel_count);
163  print_remote_int_param(v, "  inkset", pd->inkname->inkset);
164  for (i = 0; i < count; i++)
165    {
166      const stp_parameter_t *p = stp_parameter_list_param(params, i);
167      switch (p->p_type)
168	{
169	case STP_PARAMETER_TYPE_DOUBLE:
170	  if (stp_check_float_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
171	    print_remote_float_param(v, p->name,
172				     stp_get_float_parameter(v, p->name));
173	  break;
174	case STP_PARAMETER_TYPE_INT:
175	  if (stp_check_int_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
176	    print_remote_int_param(v, p->name,
177				   stp_get_int_parameter(v, p->name));
178	  break;
179	case STP_PARAMETER_TYPE_DIMENSION:
180	  if (stp_check_dimension_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
181	    print_remote_int_param(v, p->name,
182				   stp_get_dimension_parameter(v, p->name));
183	  break;
184	case STP_PARAMETER_TYPE_BOOLEAN:
185	  if (stp_check_boolean_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
186	    print_remote_int_param(v, p->name,
187				   stp_get_boolean_parameter(v, p->name));
188	  break;
189	case STP_PARAMETER_TYPE_STRING_LIST:
190	  if (stp_check_string_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
191	    print_remote_param(v, p->name,
192			       stp_get_string_parameter(v, p->name));
193	  break;
194	case STP_PARAMETER_TYPE_CURVE:
195	  if (stp_check_curve_parameter(v, p->name, STP_PARAMETER_DEFAULTED))
196	    {
197	      char *curve =
198		stp_curve_write_string(stp_get_curve_parameter(v, p->name));
199	      print_remote_param(v, p->name, curve);
200	      stp_free(curve);
201	    }
202	  break;
203	default:
204	  break;
205	}
206    }
207  stp_parameter_list_destroy(params);
208  stp_send_command(v, "\033", "ccc", 0, 0, 0);
209}
210
211static void
212escp2_set_remote_sequence(stp_vars_t *v)
213{
214  /* Magic remote mode commands, whatever they do */
215  escp2_privdata_t *pd = get_privdata(v);
216  const stp_vars_t *pv = pd->media_settings;
217
218  if (stp_get_debug_level() & STP_DBG_MARK_FILE)
219    print_debug_params(v);
220  if (pd->advanced_command_set || pd->input_slot)
221    {
222      /* Enter remote mode */
223      stp_send_command(v, "\033(R", "bcs", 0, "REMOTE1");
224      /* Per the manual, job setup comes first, then SN command */
225      if (pd->input_slot &&
226	  pd->input_slot->roll_feed_cut_flags == ROLL_FEED_CUT_ALL)
227	      stp_send_command(v, "JS", "bh", 0);
228      if (pd->preinit_remote_sequence)
229	stp_write_raw(pd->preinit_remote_sequence, v);
230      if (stp_check_int_parameter(pv, "FeedAdjustment", STP_PARAMETER_ACTIVE))
231	stp_send_command(v, "SN", "bccc", 0, 4,
232			 stp_get_int_parameter(pv, "FeedAdjustment"));
233      if (stp_check_int_parameter(pv, "VacuumIntensity", STP_PARAMETER_ACTIVE))
234	stp_send_command(v, "SN", "bccc", 0, 5,
235			 stp_get_int_parameter(pv, "VacuumIntensity"));
236      if (stp_check_float_parameter(pv, "ScanDryTime", STP_PARAMETER_ACTIVE))
237	stp_send_command(v, "DR", "bcch", 0, 0,
238			 (int) (stp_get_float_parameter(pv, "ScanDryTime") * 1000));
239      if (stp_check_float_parameter(pv, "ScanMinDryTime", STP_PARAMETER_ACTIVE))
240	stp_send_command(v, "DR", "bcch", 0, 0x40,
241			 (int) (stp_get_float_parameter(pv, "ScanMinDryTime") * 1000));
242      if (stp_check_float_parameter(pv, "PageDryTime", STP_PARAMETER_ACTIVE))
243	stp_send_command(v, "DR", "bcch", 0, 1,
244			 (int) stp_get_float_parameter(pv, "PageDryTime"));
245      /* Next comes paper path */
246      if (pd->input_slot)
247	{
248	  int divisor = pd->base_separation / 360;
249	  int height = pd->page_true_height * 5 / divisor;
250	  if (pd->input_slot->init_sequence)
251	    stp_write_raw(pd->input_slot->init_sequence, v);
252	  switch (pd->input_slot->roll_feed_cut_flags)
253	    {
254	    case ROLL_FEED_CUT_ALL:
255	      stp_send_command(v, "CO", "bccccl", 0, 0, 1, 0, 0);
256	      stp_send_command(v, "CO", "bccccl", 0, 0, 0, 0, height);
257	      break;
258	    case ROLL_FEED_CUT_LAST:
259	      stp_send_command(v, "CO", "bccccl", 0, 0, 1, 0, 0);
260	      stp_send_command(v, "CO", "bccccl", 0, 0, 2, 0, height);
261	      break;
262	    default:
263	      break;
264	    }
265	}
266      if (stp_check_int_parameter(pv, "PaperMedia", STP_PARAMETER_ACTIVE))
267	stp_send_command(v, "MI", "bcccc", 0, 1,
268			 stp_get_int_parameter(pv, "PaperMedia"),
269			 (stp_check_int_parameter(pv, "PaperMediaSize", STP_PARAMETER_ACTIVE) ?
270			  stp_get_int_parameter(pv, "PaperMediaSize") :
271			  99));	/* User-defined size (for now!) */
272      if (pd->duplex)
273	{
274	  /* If there's ever duplex no tumble, we'll need to special
275	     case it, too */
276	  if (pd->duplex == DUPLEX_TUMBLE && (pd->input_slot->duplex & DUPLEX_TUMBLE))
277	    stp_send_command(v, "DP", "bcc", 0, 2); /* Auto duplex */
278	  else
279	    stp_send_command(v, "DP", "bcc", 0, 2); /* Auto duplex */
280	}
281      if (stp_check_int_parameter(pv, "PaperThickness", STP_PARAMETER_ACTIVE))
282	stp_send_command(v, "PH", "bcc", 0,
283			 stp_get_int_parameter(pv, "PaperThickness"));
284      if (stp_check_int_parameter(pv, "FeedSequence", STP_PARAMETER_ACTIVE))
285	stp_send_command(v, "SN", "bccc", 0, 0,
286			 stp_get_int_parameter(pv, "FeedSequence"));
287      if (stp_check_int_parameter(pv, "PlatenGap", STP_PARAMETER_ACTIVE))
288	stp_send_command(v, "US", "bccc", 0, 1,
289			 stp_get_int_parameter(pv, "PlatenGap"));
290      if (stp_get_boolean_parameter(v, "FullBleed"))
291	{
292	  stp_send_command(v, "FP", "bch", 0,
293			   (unsigned short) -pd->zero_margin_offset);
294	  if (pd->borderless_sequence)
295	    stp_write_raw(pd->borderless_sequence, v);
296	}
297      if (pd->inkname->init_sequence)
298	stp_write_raw(pd->inkname->init_sequence, v);
299      /* Exit remote mode */
300
301      stp_send_command(v, "\033", "ccc", 0, 0, 0);
302    }
303}
304
305static void
306escp2_set_graphics_mode(stp_vars_t *v)
307{
308  stp_send_command(v, "\033(G", "bc", 1);
309}
310
311static void
312escp2_set_resolution(stp_vars_t *v)
313{
314  escp2_privdata_t *pd = get_privdata(v);
315  if (pd->use_extended_commands)
316    stp_send_command(v, "\033(U", "bccch",
317		     pd->unit_scale / pd->page_management_units,
318		     pd->unit_scale / pd->vertical_units,
319		     pd->unit_scale / pd->horizontal_units,
320		     pd->unit_scale);
321  else
322    stp_send_command(v, "\033(U", "bc",
323		     pd->unit_scale / pd->page_management_units);
324}
325
326static void
327escp2_set_color(stp_vars_t *v)
328{
329  escp2_privdata_t *pd = get_privdata(v);
330  if (pd->use_fast_360)
331    stp_send_command(v, "\033(K", "bcc", 0, 3);
332  else if (pd->has_graymode)
333    stp_send_command(v, "\033(K", "bcc", 0,
334		     (pd->use_black_parameters ? 1 : 2));
335}
336
337static void
338escp2_set_printer_weave(stp_vars_t *v)
339{
340  escp2_privdata_t *pd = get_privdata(v);
341  if (pd->printer_weave)
342    stp_write_raw(pd->printer_weave, v);
343  else
344    stp_send_command(v, "\033(i", "bc", 0);
345}
346
347static void
348escp2_set_printhead_speed(stp_vars_t *v)
349{
350  escp2_privdata_t *pd = get_privdata(v);
351  const char *direction = stp_get_string_parameter(v, "PrintingDirection");
352  int unidirectional = -1;
353  if (direction && strcmp(direction, "Unidirectional") == 0)
354    unidirectional = 1;
355  else if (direction && strcmp(direction, "Bidirectional") == 0)
356    unidirectional = 0;
357  else if (pd->bidirectional_upper_limit >= 0 &&
358	   pd->res->printed_hres * pd->res->printed_vres *
359	   pd->res->vertical_passes >= pd->bidirectional_upper_limit)
360    {
361      stp_dprintf(STP_DBG_ESCP2, v,
362		  "Setting unidirectional: hres %d vres %d passes %d total %d limit %d\n",
363		  pd->res->printed_hres, pd->res->printed_vres,
364		  pd->res->vertical_passes,
365		  (pd->res->printed_hres * pd->res->printed_vres *
366		   pd->res->vertical_passes),
367		  pd->bidirectional_upper_limit);
368      unidirectional = 1;
369    }
370  else if (pd->bidirectional_upper_limit >= 0)
371    {
372      stp_dprintf(STP_DBG_ESCP2, v,
373		  "Setting bidirectional: hres %d vres %d passes %d total %d limit %d\n",
374		  pd->res->printed_hres, pd->res->printed_vres,
375		  pd->res->vertical_passes,
376		  (pd->res->printed_hres * pd->res->printed_vres *
377		   pd->res->vertical_passes),
378		  pd->bidirectional_upper_limit);
379      unidirectional = 0;
380    }
381  if (unidirectional == 1)
382    {
383      stp_send_command(v, "\033U", "c", 1);
384      if (pd->res->hres > pd->physical_xdpi)
385	stp_send_command(v, "\033(s", "bc", 2);
386    }
387  else if (unidirectional == 0)
388    stp_send_command(v, "\033U", "c", 0);
389}
390
391static void
392escp2_set_dot_size(stp_vars_t *v)
393{
394  escp2_privdata_t *pd = get_privdata(v);
395  /* Dot size */
396  if (pd->drop_size >= 0)
397    stp_send_command(v, "\033(e", "bcc", 0, pd->drop_size);
398}
399
400static void
401escp2_set_page_height(stp_vars_t *v)
402{
403  escp2_privdata_t *pd = get_privdata(v);
404  int l = (pd->page_true_height + pd->paper_extra_bottom) *
405    pd->page_management_units / 72;
406  if (pd->use_extended_commands)
407    stp_send_command(v, "\033(C", "bl", l);
408  else
409    stp_send_command(v, "\033(C", "bh", l);
410}
411
412static void
413escp2_set_margins(stp_vars_t *v)
414{
415  escp2_privdata_t *pd = get_privdata(v);
416  int bot = pd->page_management_units * pd->page_bottom / 72;
417  int top = pd->page_management_units * pd->page_top / 72;
418
419  top += pd->initial_vertical_offset;
420  top -= pd->page_extra_height;
421  bot += pd->page_extra_height;
422  if (pd->use_extended_commands &&
423      (pd->command_set == MODEL_COMMAND_2000 ||
424       pd->command_set == MODEL_COMMAND_PRO))
425    stp_send_command(v, "\033(c", "bll", top, bot);
426  else
427    stp_send_command(v, "\033(c", "bhh", top, bot);
428}
429
430static void
431escp2_set_paper_dimensions(stp_vars_t *v)
432{
433  escp2_privdata_t *pd = get_privdata(v);
434  if (pd->advanced_command_set)
435    {
436      const stp_vars_t *pv = pd->media_settings;
437      int w = pd->page_true_width * pd->page_management_units / 72;
438      int h = (pd->page_true_height + pd->paper_extra_bottom) *
439	pd->page_management_units / 72;
440      stp_send_command(v, "\033(S", "bll", w, h);
441      if (stp_check_int_parameter(pv, "PrintMethod", STP_PARAMETER_ACTIVE))
442	stp_send_command(v, "\033(m", "bc",
443			 stp_get_int_parameter(pv, "PrintMethod"));
444    }
445}
446
447static void
448escp2_set_printhead_resolution(stp_vars_t *v)
449{
450  escp2_privdata_t *pd = get_privdata(v);
451  if (pd->use_extended_commands)
452    {
453      int xres;
454      int yres = pd->resolution_scale;
455
456      xres = pd->resolution_scale / pd->physical_xdpi;
457
458      if (pd->command_set == MODEL_COMMAND_PRO && pd->printer_weave)
459	yres = yres /  pd->res->vres;
460      else if (pd->split_channel_count > 1)
461	yres = yres * pd->nozzle_separation / pd->base_separation *
462	  pd->split_channel_count;
463      else
464	yres = yres * pd->nozzle_separation / pd->base_separation;
465
466      /* Magic resolution cookie */
467      stp_send_command(v, "\033(D", "bhcc", pd->resolution_scale, yres, xres);
468    }
469}
470
471static void
472set_vertical_position(stp_vars_t *v, stp_pass_t *pass)
473{
474  escp2_privdata_t *pd = get_privdata(v);
475  int advance = pass->logicalpassstart - pd->last_pass_offset -
476    (pd->separation_rows - 1);
477  advance = advance * pd->vertical_units / pd->res->printed_vres;
478  if (pass->logicalpassstart > pd->last_pass_offset ||
479      (pd->send_zero_pass_advance && pass->pass > pd->last_pass) ||
480      pd->printing_initial_vertical_offset != 0)
481    {
482      advance += pd->printing_initial_vertical_offset;
483      pd->printing_initial_vertical_offset = 0;
484      if (pd->use_extended_commands)
485	stp_send_command(v, "\033(v", "bl", advance);
486      else
487	stp_send_command(v, "\033(v", "bh", advance);
488      pd->last_pass_offset = pass->logicalpassstart;
489      pd->last_pass = pass->pass;
490    }
491}
492
493static void
494set_color(stp_vars_t *v, stp_pass_t *pass, int color)
495{
496  escp2_privdata_t *pd = get_privdata(v);
497  if (pd->last_color != color && ! pd->use_extended_commands)
498    {
499      int ncolor = pd->channels[color]->color;
500      int subchannel = pd->channels[color]->subchannel;
501      if (subchannel >= 0)
502	stp_send_command(v, "\033(r", "bcc", subchannel, ncolor);
503      else
504	stp_send_command(v, "\033r", "c", ncolor);
505      pd->last_color = color;
506    }
507}
508
509static void
510set_horizontal_position(stp_vars_t *v, stp_pass_t *pass, int vertical_subpass)
511{
512  escp2_privdata_t *pd = get_privdata(v);
513  int microoffset = (vertical_subpass & (pd->horizontal_passes - 1)) *
514    pd->image_scaled_width / pd->image_printed_width;
515  int pos = pd->image_left_position + microoffset;
516
517  if (pos != 0)
518    {
519      if (pd->command_set == MODEL_COMMAND_PRO || pd->variable_dots)
520	stp_send_command(v, "\033($", "bl", pos);
521      else if (pd->advanced_command_set || pd->res->hres > 720)
522	stp_send_command(v, "\033(\\", "bhh", pd->micro_units, pos);
523      else
524	stp_send_command(v, "\033\\", "h", pos);
525    }
526}
527
528static void
529send_print_command(stp_vars_t *v, stp_pass_t *pass, int ncolor, int nlines)
530{
531  escp2_privdata_t *pd = get_privdata(v);
532  int lwidth = (pd->image_printed_width + (pd->horizontal_passes - 1)) /
533    pd->horizontal_passes;
534  if (pd->command_set == MODEL_COMMAND_PRO || pd->variable_dots)
535    {
536      int nwidth = pd->bitwidth * ((lwidth + 7) / 8);
537      stp_send_command(v, "\033i", "ccchh", ncolor,
538		       (stp_get_debug_level() & STP_DBG_NO_COMPRESSION) ? 0 : 1,
539		       pd->bitwidth, nwidth, nlines);
540    }
541  else
542    {
543      int ygap = 3600 / pd->vertical_units;
544      int xgap = 3600 / pd->physical_xdpi;
545      if (pd->nozzles == 1)
546	{
547	  if (pd->vertical_units == 720 && pd->extra_720dpi_separation)
548	    ygap *= pd->extra_720dpi_separation;
549	}
550      else if (pd->extra_720dpi_separation)
551	ygap *= pd->extra_720dpi_separation;
552      else if (pd->pseudo_separation_rows > 0)
553	ygap *= pd->pseudo_separation_rows;
554      else
555	ygap *= pd->separation_rows;
556      stp_send_command(v, "\033.", "cccch",
557		       (stp_get_debug_level() & STP_DBG_NO_COMPRESSION) ? 0 : 1,
558		       ygap, xgap, nlines, lwidth);
559    }
560}
561
562static void
563send_extra_data(stp_vars_t *v, int extralines)
564{
565  escp2_privdata_t *pd = get_privdata(v);
566  int lwidth = (pd->image_printed_width + (pd->horizontal_passes - 1)) /
567    pd->horizontal_passes;
568  if (stp_get_debug_level() & STP_DBG_NO_COMPRESSION)
569    {
570      int i, k;
571      for (k = 0; k < extralines; k++)
572	for (i = 0; i < pd->bitwidth * (lwidth + 7) / 8; i++)
573	  stp_putc(0, v);
574    }
575  else
576    {
577      int k, l;
578      int bytes_to_fill = pd->bitwidth * ((lwidth + 7) / 8);
579      int full_blocks = bytes_to_fill / 128;
580      int leftover = bytes_to_fill % 128;
581      int total_bytes = extralines * (full_blocks + 1) * 2;
582      unsigned char *buf = stp_malloc(total_bytes);
583      total_bytes = 0;
584      for (k = 0; k < extralines; k++)
585	{
586	  for (l = 0; l < full_blocks; l++)
587	    {
588	      buf[total_bytes++] = 129;
589	      buf[total_bytes++] = 0;
590	    }
591	  if (leftover == 1)
592	    {
593	      buf[total_bytes++] = 1;
594	      buf[total_bytes++] = 0;
595	    }
596	  else if (leftover > 0)
597	    {
598	      buf[total_bytes++] = 257 - leftover;
599	      buf[total_bytes++] = 0;
600	    }
601	}
602      stp_zfwrite((const char *) buf, total_bytes, 1, v);
603      stp_free(buf);
604    }
605}
606
607void
608stpi_escp2_init_printer(stp_vars_t *v)
609{
610  escp2_reset_printer(v);
611  escp2_set_remote_sequence(v);
612  escp2_set_graphics_mode(v);
613  escp2_set_resolution(v);
614  escp2_set_color(v);
615  escp2_set_printer_weave(v);
616  escp2_set_printhead_speed(v);
617  escp2_set_dot_size(v);
618  escp2_set_printhead_resolution(v);
619  escp2_set_page_height(v);
620  escp2_set_margins(v);
621  escp2_set_paper_dimensions(v);
622}
623
624void
625stpi_escp2_deinit_printer(stp_vars_t *v)
626{
627  escp2_privdata_t *pd = get_privdata(v);
628  stp_puts("\033@", v);	/* ESC/P2 reset */
629  if (pd->advanced_command_set || pd->input_slot)
630    {
631      stp_send_command(v, "\033(R", "bcs", 0, "REMOTE1");
632      if (pd->inkname->deinit_sequence)
633	stp_write_raw(pd->inkname->deinit_sequence, v);
634      if (pd->input_slot && pd->input_slot->deinit_sequence)
635	stp_write_raw(pd->input_slot->deinit_sequence, v);
636      /* Load settings from NVRAM */
637      stp_send_command(v, "LD", "b");
638
639      /* Magic deinit sequence reported by Simone Falsini */
640      if (pd->deinit_remote_sequence)
641	stp_write_raw(pd->deinit_remote_sequence, v);
642      /* Exit remote mode */
643      stp_send_command(v, "\033", "ccc", 0, 0, 0);
644    }
645}
646
647void
648stpi_escp2_flush_pass(stp_vars_t *v, int passno, int vertical_subpass)
649{
650  int j;
651  escp2_privdata_t *pd = get_privdata(v);
652  stp_lineoff_t *lineoffs = stp_get_lineoffsets_by_pass(v, passno);
653  stp_lineactive_t *lineactive = stp_get_lineactive_by_pass(v, passno);
654  const stp_linebufs_t *bufs = stp_get_linebases_by_pass(v, passno);
655  stp_pass_t *pass = stp_get_pass_by_pass(v, passno);
656  stp_linecount_t *linecount = stp_get_linecount_by_pass(v, passno);
657  int minlines = pd->min_nozzles;
658  int nozzle_start = pd->nozzle_start;
659
660  for (j = 0; j < pd->channels_in_use; j++)
661    {
662      if (lineactive->v[j] > 0)
663	{
664	  int ncolor = pd->channels[j]->color;
665	  int subchannel = pd->channels[j]->subchannel;
666	  int nlines = linecount->v[j];
667	  int extralines = 0;
668	  set_vertical_position(v, pass);
669	  set_color(v, pass, j);
670	  if (subchannel >= 0)
671	    ncolor |= (subchannel << 4);
672
673	  if (pd->split_channels)
674	    {
675	      int sc = pd->split_channel_count;
676	      int k, l;
677	      int minlines_lo, nozzle_start_lo;
678	      minlines /= sc;
679	      nozzle_start /= sc;
680	      minlines_lo = pd->min_nozzles - (minlines * sc);
681	      nozzle_start_lo = pd->nozzle_start - (nozzle_start * sc);
682	      for (k = 0; k < sc; k++)
683		{
684		  int ml = minlines + (k < minlines_lo ? 1 : 0);
685		  int ns = nozzle_start + (k < nozzle_start_lo ? 1 : 0);
686		  int lc = ((nlines + (sc - k - 1)) / sc);
687		  int base = (pd->nozzle_start + k) % sc;
688		  if (lc < ml)
689		    extralines = ml - lc;
690		  else
691		    extralines = 0;
692		  extralines -= ns;
693		  if (extralines < 0)
694		    extralines = 0;
695		  if (lc + extralines > 0)
696		    {
697		      int sc_off = k + j * sc;
698		      set_horizontal_position(v, pass, vertical_subpass);
699		      send_print_command(v, pass, pd->split_channels[sc_off],
700					 lc + extralines + ns);
701		      if (ns > 0)
702			send_extra_data(v, ns);
703		      for (l = 0; l < lc; l++)
704			{
705			  int sp = (l * sc) + base;
706			  unsigned long offset = sp * pd->split_channel_width;
707			  if (!(stp_get_debug_level() & STP_DBG_NO_COMPRESSION))
708			    {
709			      unsigned char *comp_ptr;
710			      stp_pack_tiff(v, bufs->v[j] + offset,
711					    pd->split_channel_width,
712					    pd->comp_buf, &comp_ptr, NULL, NULL);
713			      stp_zfwrite((const char *) pd->comp_buf,
714					  comp_ptr - pd->comp_buf, 1, v);
715			    }
716			  else
717			    stp_zfwrite((const char *) bufs->v[j] + offset,
718					pd->split_channel_width, 1, v);
719			}
720		      if (extralines > 0)
721			send_extra_data(v, extralines);
722		      stp_send_command(v, "\r", "");
723		    }
724		}
725	    }
726	  else
727	    {
728	      set_horizontal_position(v, pass, vertical_subpass);
729	      if (nlines < minlines)
730		{
731		  extralines = minlines - nlines;
732		  nlines = minlines;
733		}
734	      send_print_command(v, pass, ncolor, nlines);
735	      extralines -= nozzle_start;
736	      /*
737	       * Send the data
738	       */
739	      if (nozzle_start)
740		send_extra_data(v, nozzle_start);
741	      stp_zfwrite((const char *)bufs->v[j], lineoffs->v[j], 1, v);
742	      if (extralines > 0)
743		send_extra_data(v, extralines);
744	      stp_send_command(v, "\r", "");
745	    }
746	  pd->printed_something = 1;
747	}
748      lineoffs->v[j] = 0;
749      linecount->v[j] = 0;
750    }
751}
752
753void
754stpi_escp2_terminate_page(stp_vars_t *v)
755{
756  escp2_privdata_t *pd = get_privdata(v);
757  if (!pd->input_slot ||
758      !(pd->input_slot->roll_feed_cut_flags & ROLL_FEED_DONT_EJECT))
759    {
760      if (!pd->printed_something)
761	stp_send_command(v, "\n", "");
762      stp_send_command(v, "\f", "");	/* Eject page */
763    }
764}
765