1/*
2 * "$Id: print-escp2.c,v 1.433 2010/12/11 22:04:07 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 <math.h>
37#include <limits.h>
38#include "print-escp2.h"
39
40#ifdef __GNUC__
41#define inline __inline__
42#endif
43
44#define OP_JOB_START 1
45#define OP_JOB_PRINT 2
46#define OP_JOB_END   4
47
48#ifndef MAX
49#  define MAX(a, b) ((a) > (b) ? (a) : (b))
50#endif /* !MAX */
51
52typedef struct
53{
54  unsigned count;
55  const char *name;
56} channel_count_t;
57
58static const channel_count_t escp2_channel_counts[] =
59{
60  { 1,  "1" },
61  { 2,  "2" },
62  { 3,  "3" },
63  { 4,  "4" },
64  { 5,  "5" },
65  { 6,  "6" },
66  { 7,  "7" },
67  { 8,  "8" },
68  { 9,  "9" },
69  { 10, "10" },
70  { 11, "11" },
71  { 12, "12" },
72  { 13, "13" },
73  { 14, "14" },
74  { 15, "15" },
75  { 16, "16" },
76  { 17, "17" },
77  { 18, "18" },
78  { 19, "19" },
79  { 20, "20" },
80  { 21, "21" },
81  { 22, "22" },
82  { 23, "23" },
83  { 24, "24" },
84  { 25, "25" },
85  { 26, "26" },
86  { 27, "27" },
87  { 28, "28" },
88  { 29, "29" },
89  { 30, "30" },
90  { 31, "31" },
91  { 32, "32" },
92};
93
94static stp_curve_t *hue_curve_bounds = NULL;
95static int escp2_channel_counts_count =
96sizeof(escp2_channel_counts) / sizeof(channel_count_t);
97
98static const double ink_darknesses[] =
99{
100  1.0, 0.31 / .4, 0.61 / .96, 0.08, 0.31 * 0.33 / .4, 0.61 * 0.33 / .96, 0.33, 1.0
101};
102
103#define INCH(x)		(72 * x)
104
105#define PARAMETER_INT(s)					\
106{								\
107  "escp2_" #s, "escp2_" #s,					\
108  "Color=Yes,Category=Advanced Printer Functionality", NULL,	\
109  STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,		\
110  STP_PARAMETER_LEVEL_INTERNAL, 0, 1, STP_CHANNEL_NONE, 1, 0	\
111}
112
113#define PARAMETER_INT_RO(s)					\
114{								\
115  "escp2_" #s, "escp2_" #s,					\
116  "Color=Yes,Category=Advanced Printer Functionality", NULL,	\
117  STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,		\
118  STP_PARAMETER_LEVEL_INTERNAL, 0, 1, STP_CHANNEL_NONE, 1, 1	\
119}
120
121#define PARAMETER_RAW(s)					\
122{								\
123  "escp2_" #s, "escp2_" #s,					\
124  "Color=Yes,Category=Advanced Printer Functionality", NULL,	\
125  STP_PARAMETER_TYPE_RAW, STP_PARAMETER_CLASS_FEATURE,		\
126  STP_PARAMETER_LEVEL_INTERNAL, 0, 1, STP_CHANNEL_NONE, 1, 0	\
127}
128
129typedef struct
130{
131  const stp_parameter_t param;
132  double min;
133  double max;
134  double defval;
135  int color_only;
136} float_param_t;
137
138typedef struct
139{
140  const stp_parameter_t param;
141  int min;
142  int max;
143  int defval;
144} int_param_t;
145
146static const stp_parameter_t the_parameters[] =
147{
148#if 0
149  {
150    "AutoMode", N_("Automatic Printing Mode"), "Color=Yes,Category=Basic Output Adjustment",
151    N_("Automatic printing mode"),
152    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
153    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
154  },
155#endif
156  /*
157   * Don't check this parameter.  We may offer different settings for
158   * different papers, but we need to be able to handle settings from PPD
159   * files that don't have constraints set up.
160   */
161  {
162    "Quality", N_("Print Quality"), "Color=Yes,Category=Basic Output Adjustment",
163    N_("Print Quality"),
164    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
165    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 0, 0
166  },
167  {
168    "PageSize", N_("Page Size"), "Color=No,Category=Basic Printer Setup",
169    N_("Size of the paper being printed to"),
170    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
171    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
172  },
173  {
174    "MediaType", N_("Media Type"), "Color=Yes,Category=Basic Printer Setup",
175    N_("Type of media (plain paper, photo paper, etc.)"),
176    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
177    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
178  },
179  {
180    "InputSlot", N_("Media Source"), "Color=No,Category=Basic Printer Setup",
181    N_("Source (input slot) of the media"),
182    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
183    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
184  },
185  {
186    "Duplex", N_("Double-Sided Printing"), "Color=No,Category=Basic Printer Setup",
187    N_("Duplex/Tumble Setting"),
188    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
189    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
190  },
191  {
192    "CDInnerRadius", N_("CD Hub Size"), "Color=No,Category=Basic Printer Setup",
193    N_("Print only outside of the hub of the CD, or all the way to the hole"),
194    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
195    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
196  },
197  {
198    "CDOuterDiameter", N_("CD Size (Custom)"), "Color=No,Category=Basic Printer Setup",
199    N_("Variable adjustment for the outer diameter of CD"),
200    STP_PARAMETER_TYPE_DIMENSION, STP_PARAMETER_CLASS_FEATURE,
201    STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
202  },
203  {
204    "CDInnerDiameter", N_("CD Hub Size (Custom)"), "Color=No,Category=Basic Printer Setup",
205    N_("Variable adjustment to the inner hub of the CD"),
206    STP_PARAMETER_TYPE_DIMENSION, STP_PARAMETER_CLASS_FEATURE,
207    STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
208  },
209  {
210    "CDXAdjustment", N_("CD Horizontal Fine Adjustment"), "Color=No,Category=Advanced Printer Setup",
211    N_("Fine adjustment to horizontal position for CD printing"),
212    STP_PARAMETER_TYPE_DIMENSION, STP_PARAMETER_CLASS_FEATURE,
213    STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
214  },
215  {
216    "CDYAdjustment", N_("CD Vertical Fine Adjustment"), "Color=No,Category=Advanced Printer Setup",
217    N_("Fine adjustment to horizontal position for CD printing"),
218    STP_PARAMETER_TYPE_DIMENSION, STP_PARAMETER_CLASS_FEATURE,
219    STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
220  },
221  {
222    "CDAllowOtherMedia", N_("CD Allow Other Media Sizes"), "Color=No,Category=Advanced Printer Setup",
223    N_("Allow non-CD media sizes when printing to CD"),
224    STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
225    STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
226  },
227  {
228    "Resolution", N_("Resolution"), "Color=Yes,Category=Basic Printer Setup",
229    N_("Resolution of the print"),
230    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
231    STP_PARAMETER_LEVEL_ADVANCED, 1, 1, STP_CHANNEL_NONE, 1, 0
232  },
233  /*
234   * Don't check this parameter.  We may offer different settings for
235   * different ink sets, but we need to be able to handle settings from PPD
236   * files that don't have constraints set up.
237   */
238  {
239    "InkType", N_("Ink Type"), "Color=Yes,Category=Advanced Printer Setup",
240    N_("Type of ink in the printer"),
241    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
242    STP_PARAMETER_LEVEL_ADVANCED2, 1, 1, STP_CHANNEL_NONE, 0, 0
243  },
244  {
245    "UseGloss", N_("Enhanced Gloss"), "Color=Yes,Category=Basic Printer Setup",
246    N_("Add gloss enhancement"),
247    STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
248    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 0, 0
249  },
250  {
251    "InkSet", N_("Ink Set"), "Color=Yes,Category=Basic Printer Setup",
252    N_("Type of ink in the printer"),
253    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
254    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
255  },
256  {
257    "PrintingDirection", N_("Printing Direction"), "Color=Yes,Category=Advanced Output Adjustment",
258    N_("Printing direction (unidirectional is higher quality, but slower)"),
259    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
260    STP_PARAMETER_LEVEL_ADVANCED1, 1, 1, STP_CHANNEL_NONE, 1, 0
261  },
262  {
263    "FullBleed", N_("Borderless"), "Color=No,Category=Basic Printer Setup",
264    N_("Print without borders"),
265    STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
266    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
267  },
268  {
269    "Weave", N_("Interleave Method"), "Color=Yes,Category=Advanced Output Adjustment",
270    N_("Interleave pattern to use"),
271    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
272    STP_PARAMETER_LEVEL_ADVANCED1, 1, 1, STP_CHANNEL_NONE, 1, 0
273  },
274  {
275    "OutputOrder", N_("Output Order"), "Color=No,Category=Basic Printer Setup",
276    N_("Output Order"),
277    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
278    STP_PARAMETER_LEVEL_BASIC, 0, 0, STP_CHANNEL_NONE, 0, 0
279  },
280  {
281    "AlignmentPasses", N_("Alignment Passes"), "Color=No,Category=Advanced Printer Functionality",
282    N_("Alignment Passes"),
283    STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
284    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
285  },
286  {
287    "AlignmentChoices", N_("Alignment Choices"), "Color=No,Category=Advanced Printer Functionality",
288    N_("Alignment Choices"),
289    STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
290    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
291  },
292  {
293    "InkChange", N_("Ink change command"), "Color=No,Category=Advanced Printer Functionality",
294    N_("Ink change command"),
295    STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
296    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
297  },
298  {
299    "AlternateAlignmentPasses", N_("Alternate Alignment Passes"), "Color=No,Category=Advanced Printer Functionality",
300    N_("Alternate Alignment Passes"),
301    STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
302    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
303  },
304  {
305    "AlternateAlignmentChoices", N_("Alternate Alignment Choices"), "Color=No,Category=Advanced Printer Functionality",
306    N_("Alternate Alignment Choices"),
307    STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
308    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
309  },
310  {
311    "SupportsPacketMode", N_("Supports Packet Mode"), "Color=No,Category=Advanced Printer Functionality",
312    N_("Supports D4 Packet Mode"),
313    STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
314    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
315  },
316  {
317    "InterchangeableInk", N_("Has Interchangeable Ink Cartridges"), "Color=No,Category=Advanced Printer Functionality",
318    N_("Has multiple choices of ink cartridges"),
319    STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_FEATURE,
320    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
321  },
322  {
323    "InkChannels", N_("Ink Channels"), "Color=No,Category=Advanced Printer Functionality",
324    N_("Ink Channels"),
325    STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
326    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
327  },
328  {
329    "RawChannelNames", N_("Raw Channel Names"), "Color=No,Category=Advanced Printer Functionality",
330    N_("Raw Channel Names"),
331    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
332    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
333  },
334  {
335    "ChannelNames", N_("Channel Names"), "Color=No,Category=Advanced Printer Functionality",
336    N_("Channel Names"),
337    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
338    STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0
339  },
340  {
341    "PrintingMode", N_("Printing Mode"), "Color=Yes,Category=Core Parameter",
342    N_("Printing Output Mode"),
343    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
344    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
345  },
346  {
347    "RawChannels", N_("Raw Channels"), "Color=Yes,Category=Core Parameter",
348    N_("Raw Channel Count"),
349    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
350    STP_PARAMETER_LEVEL_BASIC, 0, 1, STP_CHANNEL_NONE, 1, 0
351  },
352  {
353    "CyanHueCurve", N_("Cyan Map"), "Color=Yes,Category=Advanced Output Control",
354    N_("Adjust the cyan map"),
355    STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
356    STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 1, 1, 0
357  },
358  {
359    "MagentaHueCurve", N_("Magenta Map"), "Color=Yes,Category=Advanced Output Control",
360    N_("Adjust the magenta map"),
361    STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
362    STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 2, 1, 0
363  },
364  {
365    "YellowHueCurve", N_("Yellow Map"), "Color=Yes,Category=Advanced Output Control",
366    N_("Adjust the yellow map"),
367    STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
368    STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 3, 1, 0
369  },
370  {
371    "BlueHueCurve", N_("Blue Map"), "Color=Yes,Category=Advanced Output Control",
372    N_("Adjust the blue map"),
373    STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
374    STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 4, 1, 0
375  },
376  {
377    "OrangeHueCurve", N_("Orange Map"), "Color=Yes,Category=Advanced Output Control",
378    N_("Adjust the orange map"),
379    STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
380    STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 4, 1, 0
381  },
382  {
383    "RedHueCurve", N_("Red Map"), "Color=Yes,Category=Advanced Output Control",
384    N_("Adjust the red map"),
385    STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
386    STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 5, 1, 0
387  },
388  PARAMETER_INT(max_hres),
389  PARAMETER_INT(max_vres),
390  PARAMETER_INT(min_hres),
391  PARAMETER_INT(min_vres),
392  PARAMETER_INT(nozzles),
393  PARAMETER_INT(black_nozzles),
394  PARAMETER_INT(fast_nozzles),
395  PARAMETER_INT(min_nozzles),
396  PARAMETER_INT(min_black_nozzles),
397  PARAMETER_INT(min_fast_nozzles),
398  PARAMETER_INT(nozzle_start),
399  PARAMETER_INT(black_nozzle_start),
400  PARAMETER_INT(fast_nozzle_start),
401  PARAMETER_INT(nozzle_separation),
402  PARAMETER_INT(black_nozzle_separation),
403  PARAMETER_INT(fast_nozzle_separation),
404  PARAMETER_INT(separation_rows),
405  PARAMETER_INT(max_paper_width),
406  PARAMETER_INT(max_paper_height),
407  PARAMETER_INT(min_paper_width),
408  PARAMETER_INT(min_paper_height),
409  PARAMETER_INT(max_imageable_width),
410  PARAMETER_INT(max_imageable_height),
411  PARAMETER_INT(extra_feed),
412  PARAMETER_INT(pseudo_separation_rows),
413  PARAMETER_INT(base_separation),
414  PARAMETER_INT(resolution_scale),
415  PARAMETER_INT(initial_vertical_offset),
416  PARAMETER_INT(black_initial_vertical_offset),
417  PARAMETER_INT(max_black_resolution),
418  PARAMETER_INT(zero_margin_offset),
419  PARAMETER_INT(extra_720dpi_separation),
420  PARAMETER_INT(micro_left_margin),
421  PARAMETER_INT(min_horizontal_position_alignment),
422  PARAMETER_INT(base_horizontal_position_alignment),
423  PARAMETER_INT(bidirectional_upper_limit),
424  PARAMETER_INT(physical_channels),
425  PARAMETER_INT(left_margin),
426  PARAMETER_INT(right_margin),
427  PARAMETER_INT(top_margin),
428  PARAMETER_INT(bottom_margin),
429  PARAMETER_INT(ink_type),
430  PARAMETER_INT(bits),
431  PARAMETER_INT(base_res),
432  PARAMETER_INT_RO(alignment_passes),
433  PARAMETER_INT_RO(alignment_choices),
434  PARAMETER_INT_RO(alternate_alignment_passes),
435  PARAMETER_INT_RO(alternate_alignment_choices),
436  PARAMETER_INT(cd_x_offset),
437  PARAMETER_INT(cd_y_offset),
438  PARAMETER_INT(cd_page_width),
439  PARAMETER_INT(cd_page_height),
440  PARAMETER_INT(paper_extra_bottom),
441  PARAMETER_RAW(preinit_sequence),
442  PARAMETER_RAW(preinit_remote_sequence),
443  PARAMETER_RAW(postinit_remote_sequence),
444  PARAMETER_RAW(vertical_borderless_sequence)
445};
446
447static const int the_parameter_count =
448sizeof(the_parameters) / sizeof(const stp_parameter_t);
449
450static const float_param_t float_parameters[] =
451{
452  {
453    {
454      "CyanDensity", N_("Cyan Density"), "Color=Yes,Category=Output Level Adjustment",
455      N_("Adjust the cyan density"),
456      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
457      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 1, 1, 0
458    }, 0.0, 2.0, 1.0, 1
459  },
460  {
461    {
462      "MagentaDensity", N_("Magenta Density"), "Color=Yes,Category=Output Level Adjustment",
463      N_("Adjust the magenta density"),
464      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
465      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 2, 1, 0
466    }, 0.0, 2.0, 1.0, 1
467  },
468  {
469    {
470      "YellowDensity", N_("Yellow Density"), "Color=Yes,Category=Output Level Adjustment",
471      N_("Adjust the yellow density"),
472      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
473      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 3, 1, 0
474    }, 0.0, 2.0, 1.0, 1
475  },
476  {
477    {
478      "BlackDensity", N_("Black Density"), "Color=Yes,Category=Output Level Adjustment",
479      N_("Adjust the black density"),
480      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
481      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 0, 1, 0
482    }, 0.0, 2.0, 1.0, 1
483  },
484  {
485    {
486      "RedDensity", N_("Red Density"), "Color=Yes,Category=Output Level Adjustment",
487      N_("Adjust the red density"),
488      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
489      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 4, 1, 0
490    }, 0.0, 2.0, 1.0, 1
491  },
492  {
493    {
494      "BlueDensity", N_("Blue Density"), "Color=Yes,Category=Output Level Adjustment",
495      N_("Adjust the blue density"),
496      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
497      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 5, 1, 0
498    }, 0.0, 2.0, 1.0, 1
499  },
500  {
501    {
502      "OrangeDensity", N_("Orange Density"), "Color=Yes,Category=Output Level Adjustment",
503      N_("Adjust the orange density"),
504      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
505      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 5, 1, 0
506    }, 0.0, 2.0, 1.0, 1
507  },
508  {
509    {
510      "GlossLimit", N_("Gloss Level"), "Color=Yes,Category=Output Level Adjustment",
511      N_("Adjust the gloss level"),
512      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
513      STP_PARAMETER_LEVEL_ADVANCED, 0, 1, 6, 1, 0
514    }, 0.0, 2.0, 1.0, 1
515  },
516  {
517    {
518      "DropSize1", N_("Drop Size Small"), "Color=Yes,Category=Advanced Ink Adjustment",
519      N_("Drop Size 1 (small)"),
520      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
521      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
522    }, 0.0, 1.0, 1.0, 1
523  },
524  {
525    {
526      "DropSize2", N_("Drop Size Medium"), "Color=Yes,Category=Advanced Ink Adjustment",
527      N_("Drop Size 2 (medium)"),
528      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
529      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
530    }, 0.0, 1.0, 0.0, 1
531  },
532  {
533    {
534      "DropSize3", N_("Drop Size Large"), "Color=Yes,Category=Advanced Ink Adjustment",
535      N_("Drop Size 3 (large)"),
536      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
537      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
538    }, 0.0, 1.0, 0.0, 1
539  },
540  {
541    {
542      "LightCyanValue", N_("Light Cyan Value"), "Color=Yes,Category=Advanced Ink Adjustment",
543      N_("Light Cyan Value"),
544      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
545      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
546    }, 0.0, 5.0, 1.0, 1
547  },
548  {
549    {
550      "LightCyanTrans", N_("Light Cyan Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
551      N_("Light Cyan Transition"),
552      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
553      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
554    }, 0.0, 5.0, 1.0, 1
555  },
556  {
557    {
558      "LightCyanScale", N_("Light Cyan Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
559      N_("Light Cyan Density Scale"),
560      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
561      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
562    }, 0.0, 5.0, 1.0, 1
563  },
564  {
565    {
566      "LightMagentaValue", N_("Light Magenta Value"), "Color=Yes,Category=Advanced Ink Adjustment",
567      N_("Light Magenta Value"),
568      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
569      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
570    }, 0.0, 5.0, 1.0, 1
571  },
572  {
573    {
574      "LightMagentaScale", N_("Light Magenta Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
575      N_("Light Magenta Density Scale"),
576      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
577      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
578    }, 0.0, 5.0, 1.0, 1
579  },
580  {
581    {
582      "LightMagentaTrans", N_("Light Magenta Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
583      N_("Light Magenta Transition"),
584      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
585      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
586    }, 0.0, 5.0, 1.0, 1
587  },
588  {
589    {
590      "DarkYellowValue", N_("Dark Yellow Value"), "Color=Yes,Category=Advanced Ink Adjustment",
591      N_("Dark Yellow Value"),
592      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
593      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
594    }, 0.0, 5.0, 1.0, 1
595  },
596  {
597    {
598      "DarkYellowTrans", N_("Dark Yellow Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
599      N_("Dark Yellow Transition"),
600      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
601      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
602    }, 0.0, 5.0, 1.0, 1
603  },
604  {
605    {
606      "DarkYellowScale", N_("Dark Yellow Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
607      N_("Dark Yellow Density Scale"),
608      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
609      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
610    }, 0.0, 5.0, 1.0, 1
611  },
612  {
613    {
614      "GrayValue", N_("Gray Value"), "Color=Yes,Category=Advanced Ink Adjustment",
615      N_("Gray Value"),
616      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
617      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
618    }, 0.0, 5.0, 1.0, 1
619  },
620  {
621    {
622      "GrayTrans", N_("Gray Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
623      N_("Gray Transition"),
624      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
625      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
626    }, 0.0, 5.0, 1.0, 1
627  },
628  {
629    {
630      "GrayScale", N_("Gray Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
631      N_("Gray Density Scale"),
632      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
633      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
634    }, 0.0, 5.0, 1.0, 1
635  },
636  {
637    {
638      "DarkGrayValue", N_("Gray Value"), "Color=Yes,Category=Advanced Ink Adjustment",
639      N_("Gray Value"),
640      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
641      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
642    }, 0.0, 5.0, 1.0, 1
643  },
644  {
645    {
646      "DarkGrayTrans", N_("Gray Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
647      N_("Gray Transition"),
648      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
649      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
650    }, 0.0, 5.0, 1.0, 1
651  },
652  {
653    {
654      "DarkGrayScale", N_("Gray Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
655      N_("Gray Density Scale"),
656      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
657      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
658    }, 0.0, 5.0, 1.0, 1
659  },
660  {
661    {
662      "LightGrayValue", N_("Light Gray Value"), "Color=Yes,Category=Advanced Ink Adjustment",
663      N_("Light Gray Value"),
664      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
665      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
666    }, 0.0, 5.0, 1.0, 1
667  },
668  {
669    {
670      "LightGrayTrans", N_("Light Gray Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
671      N_("Light Gray Transition"),
672      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
673      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
674    }, 0.0, 5.0, 1.0, 1
675  },
676  {
677    {
678      "LightGrayScale", N_("Light Gray Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
679      N_("Light Gray Density Scale"),
680      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
681      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
682    }, 0.0, 5.0, 1.0, 1
683  },
684  {
685    {
686      "Gray3Value", N_("Dark Gray Value"), "Color=Yes,Category=Advanced Ink Adjustment",
687      N_("Dark Gray Value"),
688      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
689      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
690    }, 0.0, 5.0, 1.0, 1
691  },
692  {
693    {
694      "Gray3Trans", N_("Dark Gray Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
695      N_("Dark Gray Transition"),
696      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
697      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
698    }, 0.0, 5.0, 1.0, 1
699  },
700  {
701    {
702      "Gray3Scale", N_("Dark Gray Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
703      N_("Dark Gray Density Scale"),
704      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
705      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
706    }, 0.0, 5.0, 1.0, 1
707  },
708  {
709    {
710      "Gray2Value", N_("Mid Gray Value"), "Color=Yes,Category=Advanced Ink Adjustment",
711      N_("Medium Gray Value"),
712      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
713      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
714    }, 0.0, 5.0, 1.0, 1
715  },
716  {
717    {
718      "Gray2Trans", N_("Mid Gray Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
719      N_("Medium Gray Transition"),
720      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
721      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
722    }, 0.0, 5.0, 1.0, 1
723  },
724  {
725    {
726      "Gray2Scale", N_("Mid Gray Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
727      N_("Medium Gray Density Scale"),
728      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
729      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
730    }, 0.0, 5.0, 1.0, 1
731  },
732  {
733    {
734      "Gray1Value", N_("Light Gray Value"), "Color=Yes,Category=Advanced Ink Adjustment",
735      N_("Light Gray Value"),
736      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
737      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
738    }, 0.0, 5.0, 1.0, 1
739  },
740  {
741    {
742      "Gray1Trans", N_("Light Gray Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
743      N_("Light Gray Transition"),
744      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
745      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
746    }, 0.0, 5.0, 1.0, 1
747  },
748  {
749    {
750      "Gray1Scale", N_("Light Gray Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
751      N_("Light Gray Density Scale"),
752      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
753      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
754    }, 0.0, 5.0, 1.0, 1
755  },
756  {
757    {
758      "HGray5Value", N_("Hextone Gray 5 Value"), "Color=Yes,Category=Advanced Ink Adjustment",
759      N_("Hextone Gray 5 (Darkest) Value"),
760      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
761      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
762    }, 0.0, 5.0, 1.0, 1
763  },
764  {
765    {
766      "HGray5Trans", N_("Hextone Gray 5 Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
767      N_("Hextone Gray 5 (Darkest) Transition"),
768      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
769      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
770    }, 0.0, 5.0, 1.0, 1
771  },
772  {
773    {
774      "HGray5Scale", N_("Hextone Gray 5 Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
775      N_("Hextone Gray 5 (Darkest) Density Scale"),
776      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
777      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
778    }, 0.0, 5.0, 1.0, 1
779  },
780  {
781    {
782      "HGray4Value", N_("Hextone Gray 4 Value"), "Color=Yes,Category=Advanced Ink Adjustment",
783      N_("Hextone Gray 4 Value"),
784      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
785      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
786    }, 0.0, 5.0, 1.0, 1
787  },
788  {
789    {
790      "HGray4Trans", N_("Hextone Gray 4 Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
791      N_("Hextone Gray 4 Transition"),
792      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
793      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
794    }, 0.0, 5.0, 1.0, 1
795  },
796  {
797    {
798      "HGray4Scale", N_("Hextone Gray 4 Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
799      N_("Hextone Gray 4 Density Scale"),
800      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
801      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
802    }, 0.0, 5.0, 1.0, 1
803  },
804  {
805    {
806      "HGray3Value", N_("Hextone Gray 3 Value"), "Color=Yes,Category=Advanced Ink Adjustment",
807      N_("Hextone Gray 3 Value"),
808      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
809      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
810    }, 0.0, 5.0, 1.0, 1
811  },
812  {
813    {
814      "HGray3Trans", N_("Hextone Gray 3 Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
815      N_("Hextone Gray 3 Transition"),
816      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
817      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
818    }, 0.0, 5.0, 1.0, 1
819  },
820  {
821    {
822      "HGray3Scale", N_("Hextone Gray 3 Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
823      N_("Hextone Gray 3 Density Scale"),
824      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
825      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
826    }, 0.0, 5.0, 1.0, 1
827  },
828  {
829    {
830      "HGray2Value", N_("Hextone Gray 2 Value"), "Color=Yes,Category=Advanced Ink Adjustment",
831      N_("Hextone Gray 2 Value"),
832      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
833      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
834    }, 0.0, 5.0, 1.0, 1
835  },
836  {
837    {
838      "HGray2Trans", N_("Hextone Gray 2 Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
839      N_("Hextone Gray 2 Transition"),
840      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
841      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
842    }, 0.0, 5.0, 1.0, 1
843  },
844  {
845    {
846      "HGray2Scale", N_("Hextone Gray 2 Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
847      N_("Hextone Gray 2 Density Scale"),
848      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
849      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
850    }, 0.0, 5.0, 1.0, 1
851  },
852  {
853    {
854      "HGray1Value", N_("Hextone Gray 1 Value"), "Color=Yes,Category=Advanced Ink Adjustment",
855      N_("Hextone Gray 1 (Lightest) Value"),
856      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
857      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
858    }, 0.0, 5.0, 1.0, 1
859  },
860  {
861    {
862      "HGray1Trans", N_("Hextone Gray 1 Transition"), "Color=Yes,Category=Advanced Ink Adjustment",
863      N_("Hextone Gray 1 (Lightest) Transition"),
864      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
865      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
866    }, 0.0, 5.0, 1.0, 1
867  },
868  {
869    {
870      "HGray1Scale", N_("Hextone Gray 1 Density Scale"), "Color=Yes,Category=Advanced Ink Adjustment",
871      N_("Hextone Gray 1 (Lightest) Density Scale"),
872      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
873      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, STP_CHANNEL_NONE, 1, 0
874    }, 0.0, 5.0, 1.0, 1
875  },
876  {
877    {
878      "BlackTrans", N_("GCR Transition"), "Color=Yes,Category=Advanced Output Control",
879      N_("Adjust the gray component transition rate"),
880      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
881      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
882    }, 0.0, 1.0, 1.0, 1
883  },
884  {
885    {
886      "GCRLower", N_("GCR Lower Bound"), "Color=Yes,Category=Advanced Output Control",
887      N_("Lower bound of gray component reduction"),
888      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
889      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
890    }, 0.0, 1.0, 0.2, 1
891  },
892  {
893    {
894      "GCRUpper", N_("GCR Upper Bound"), "Color=Yes,Category=Advanced Output Control",
895      N_("Upper bound of gray component reduction"),
896      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
897      STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
898    }, 0.0, 5.0, 0.5, 1
899  },
900  {
901    {
902      "PageDryTime", N_("Drying Time Per Page"), "Color=No,Category=Advanced Printer Functionality",
903      N_("Set drying time per page"),
904      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_FEATURE,
905      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
906    }, 0, 60.0, 0.0, 1
907  },
908  {
909    {
910      "ScanDryTime", N_("Drying Time Per Scan"), "Color=No,Category=Advanced Printer Functionality",
911      N_("Set drying time per scan"),
912      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_FEATURE,
913      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
914    }, 0, 10.0, 0.0, 1
915  },
916  {
917    {
918      "ScanMinDryTime", N_("Minimum Drying Time Per Scan"), "Color=No,Category=Advanced Printer Functionality",
919      N_("Set minimum drying time per scan"),
920      STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_FEATURE,
921      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
922    }, 0, 10.0, 0.0, 1
923  },
924};
925
926static const int float_parameter_count =
927sizeof(float_parameters) / sizeof(const float_param_t);
928
929
930static const int_param_t int_parameters[] =
931{
932  {
933    {
934      "BandEnhancement", N_("Quality Enhancement"), "Color=No,Category=Advanced Printer Functionality",
935      N_("Enhance print quality by additional passes"),
936      STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
937      STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, STP_CHANNEL_NONE, 1, 0
938    }, 0, 4, 0
939  },
940  {
941    {
942      "PaperThickness", N_("Paper Thickness"), "Color=No,Category=Advanced Printer Functionality",
943      N_("Set printer paper thickness"),
944      STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
945      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
946    }, 0, 255, 0
947  },
948  {
949    {
950      "VacuumIntensity", N_("Vacuum Intensity"), "Color=No,Category=Advanced Printer Functionality",
951      N_("Set vacuum intensity (printer-specific)"),
952      STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
953      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
954    }, 0, 255, 0
955  },
956  {
957    {
958      "FeedSequence", N_("Feed Sequence"), "Color=No,Category=Advanced Printer Functionality",
959      N_("Set paper feed sequence (printer-specific)"),
960      STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
961      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
962    }, 0, 255, 0
963  },
964  {
965    {
966      "PrintMethod", N_("Print Method"), "Color=No,Category=Advanced Printer Functionality",
967      N_("Set print method (printer-specific)"),
968      STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
969      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
970    }, 0, 255, 0
971  },
972  {
973    {
974      "PlatenGap", N_("Platen Gap"), "Color=No,Category=Advanced Printer Functionality",
975      N_("Set platen gap (printer-specific)"),
976      STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
977      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
978    }, 0, 255, 0
979  },
980  {
981    {
982      "FeedAdjustment", N_("Feed Adjustment"), "Color=No,Category=Advanced Printer Functionality",
983      /* xgettext:no-c-format */
984      N_("Set paper feed adjustment (0.01% units)"),
985      STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
986      STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, STP_CHANNEL_NONE, 1, 0
987    }, 0, 255, 0
988  },
989};
990
991static const int int_parameter_count =
992sizeof(int_parameters) / sizeof(const int_param_t);
993
994
995static escp2_privdata_t *
996get_privdata(stp_vars_t *v)
997{
998  return (escp2_privdata_t *) stp_get_component_data(v, "Driver");
999}
1000
1001#define DEF_SIMPLE_ACCESSOR(f, t)					\
1002static inline t								\
1003escp2_##f(const stp_vars_t *v)						\
1004{									\
1005  if (stp_check_int_parameter(v, "escp2_" #f, STP_PARAMETER_ACTIVE))	\
1006    return stp_get_int_parameter(v, "escp2_" #f);			\
1007  else									\
1008    {									\
1009      stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);	\
1010      return printdef->f;						\
1011    }									\
1012}
1013
1014#define DEF_RAW_ACCESSOR(f, t)						\
1015static inline t								\
1016escp2_##f(const stp_vars_t *v)						\
1017{									\
1018  if (stp_check_raw_parameter(v, "escp2_" #f, STP_PARAMETER_ACTIVE))	\
1019    return stp_get_raw_parameter(v, "escp2_" #f);			\
1020  else									\
1021    {									\
1022      stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);	\
1023      return printdef->f;						\
1024    }									\
1025}
1026
1027#define DEF_ROLL_ACCESSOR(f, t)						\
1028static inline t								\
1029escp2_##f(const stp_vars_t *v, int rollfeed)				\
1030{									\
1031  if (stp_check_int_parameter(v, "escp2_" #f, STP_PARAMETER_ACTIVE))	\
1032    return stp_get_int_parameter(v, "escp2_" #f);			\
1033  else									\
1034    {									\
1035      stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);	\
1036      const res_t *res = stp_escp2_find_resolution(v);			\
1037      if (res && res->command)						\
1038	{								\
1039	  if (rollfeed)							\
1040	    return (printdef->m_roll_##f);				\
1041	  else								\
1042	    return (printdef->m_##f);					\
1043	}								\
1044      else								\
1045	{								\
1046	  if (rollfeed)							\
1047	    return (printdef->roll_##f);				\
1048	  else								\
1049	    return (printdef->f);					\
1050	}								\
1051    }									\
1052}
1053
1054DEF_SIMPLE_ACCESSOR(max_hres, int)
1055DEF_SIMPLE_ACCESSOR(max_vres, int)
1056DEF_SIMPLE_ACCESSOR(min_hres, int)
1057DEF_SIMPLE_ACCESSOR(min_vres, int)
1058DEF_SIMPLE_ACCESSOR(nozzles, unsigned)
1059DEF_SIMPLE_ACCESSOR(black_nozzles, unsigned)
1060DEF_SIMPLE_ACCESSOR(fast_nozzles, unsigned)
1061DEF_SIMPLE_ACCESSOR(min_nozzles, unsigned)
1062DEF_SIMPLE_ACCESSOR(min_black_nozzles, unsigned)
1063DEF_SIMPLE_ACCESSOR(min_fast_nozzles, unsigned)
1064DEF_SIMPLE_ACCESSOR(nozzle_start, int)
1065DEF_SIMPLE_ACCESSOR(black_nozzle_start, int)
1066DEF_SIMPLE_ACCESSOR(fast_nozzle_start, int)
1067DEF_SIMPLE_ACCESSOR(nozzle_separation, unsigned)
1068DEF_SIMPLE_ACCESSOR(black_nozzle_separation, unsigned)
1069DEF_SIMPLE_ACCESSOR(fast_nozzle_separation, unsigned)
1070DEF_SIMPLE_ACCESSOR(separation_rows, unsigned)
1071DEF_SIMPLE_ACCESSOR(max_paper_width, unsigned)
1072DEF_SIMPLE_ACCESSOR(max_paper_height, unsigned)
1073DEF_SIMPLE_ACCESSOR(min_paper_width, unsigned)
1074DEF_SIMPLE_ACCESSOR(min_paper_height, unsigned)
1075DEF_SIMPLE_ACCESSOR(max_imageable_width, unsigned)
1076DEF_SIMPLE_ACCESSOR(max_imageable_height, unsigned)
1077DEF_SIMPLE_ACCESSOR(cd_x_offset, int)
1078DEF_SIMPLE_ACCESSOR(cd_y_offset, int)
1079DEF_SIMPLE_ACCESSOR(cd_page_width, int)
1080DEF_SIMPLE_ACCESSOR(cd_page_height, int)
1081DEF_SIMPLE_ACCESSOR(paper_extra_bottom, int)
1082DEF_SIMPLE_ACCESSOR(extra_feed, unsigned)
1083DEF_SIMPLE_ACCESSOR(pseudo_separation_rows, int)
1084DEF_SIMPLE_ACCESSOR(base_separation, int)
1085DEF_SIMPLE_ACCESSOR(resolution_scale, int)
1086DEF_SIMPLE_ACCESSOR(initial_vertical_offset, int)
1087DEF_SIMPLE_ACCESSOR(black_initial_vertical_offset, int)
1088DEF_SIMPLE_ACCESSOR(max_black_resolution, int)
1089DEF_SIMPLE_ACCESSOR(zero_margin_offset, int)
1090DEF_SIMPLE_ACCESSOR(extra_720dpi_separation, int)
1091DEF_SIMPLE_ACCESSOR(micro_left_margin, int)
1092DEF_SIMPLE_ACCESSOR(min_horizontal_position_alignment, unsigned)
1093DEF_SIMPLE_ACCESSOR(base_horizontal_position_alignment, unsigned)
1094DEF_SIMPLE_ACCESSOR(bidirectional_upper_limit, int)
1095DEF_SIMPLE_ACCESSOR(physical_channels, int)
1096DEF_SIMPLE_ACCESSOR(alignment_passes, int)
1097DEF_SIMPLE_ACCESSOR(alignment_choices, int)
1098DEF_SIMPLE_ACCESSOR(alternate_alignment_passes, int)
1099DEF_SIMPLE_ACCESSOR(alternate_alignment_choices, int)
1100
1101DEF_ROLL_ACCESSOR(left_margin, unsigned)
1102DEF_ROLL_ACCESSOR(right_margin, unsigned)
1103DEF_ROLL_ACCESSOR(top_margin, unsigned)
1104DEF_ROLL_ACCESSOR(bottom_margin, unsigned)
1105
1106DEF_RAW_ACCESSOR(preinit_sequence, const stp_raw_t *)
1107DEF_RAW_ACCESSOR(preinit_remote_sequence, const stp_raw_t *)
1108DEF_RAW_ACCESSOR(postinit_remote_sequence, const stp_raw_t *)
1109
1110DEF_RAW_ACCESSOR(vertical_borderless_sequence, const stp_raw_t *)
1111
1112static const resolution_list_t *
1113escp2_reslist(const stp_vars_t *v)
1114{
1115  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1116  return printdef->resolutions;
1117}
1118
1119static inline const printer_weave_list_t *
1120escp2_printer_weaves(const stp_vars_t *v)
1121{
1122  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1123  return printdef->printer_weaves;
1124}
1125
1126static inline const stp_string_list_t *
1127escp2_channel_names(const stp_vars_t *v)
1128{
1129  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1130  return (printdef->channel_names);
1131}
1132
1133static inline const inkgroup_t *
1134escp2_inkgroup(const stp_vars_t *v)
1135{
1136  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1137  return (printdef->inkgroup);
1138}
1139
1140static inline const quality_list_t *
1141escp2_quality_list(const stp_vars_t *v)
1142{
1143  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1144  return printdef->quality_list;
1145}
1146
1147static short
1148escp2_duplex_left_margin(const stp_vars_t *v)
1149{
1150  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1151  return printdef->duplex_left_margin;
1152}
1153
1154static short
1155escp2_duplex_right_margin(const stp_vars_t *v)
1156{
1157  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1158  return printdef->duplex_right_margin;
1159}
1160
1161static short
1162escp2_duplex_top_margin(const stp_vars_t *v)
1163{
1164  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1165  return printdef->duplex_top_margin;
1166}
1167
1168static short
1169escp2_duplex_bottom_margin(const stp_vars_t *v)
1170{
1171  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1172  return printdef->duplex_bottom_margin;
1173}
1174
1175static const channel_count_t *
1176get_channel_count_by_name(const char *name)
1177{
1178  int i;
1179  for (i = 0; i < escp2_channel_counts_count; i++)
1180    if (strcmp(name, escp2_channel_counts[i].name) == 0)
1181      return &(escp2_channel_counts[i]);
1182  return NULL;
1183}
1184
1185static const channel_count_t *
1186get_channel_count_by_number(unsigned count)
1187{
1188  int i;
1189  for (i = 0; i < escp2_channel_counts_count; i++)
1190    if (count == escp2_channel_counts[i].count)
1191      return &(escp2_channel_counts[i]);
1192  return NULL;
1193}
1194
1195static int
1196escp2_res_param(const stp_vars_t *v, const char *param, const res_t *res)
1197{
1198  if (res)
1199    {
1200      if (res->v &&
1201	  stp_check_int_parameter(res->v, param, STP_PARAMETER_ACTIVE))
1202	return stp_get_int_parameter(res->v, param);
1203      else
1204	return -1;
1205    }
1206  if (stp_check_int_parameter(v, param, STP_PARAMETER_ACTIVE))
1207    return stp_get_int_parameter(v, param);
1208  else
1209    {
1210      const res_t *res1 = stp_escp2_find_resolution(v);
1211      if (res1->v &&
1212	  stp_check_int_parameter(res1->v, param, STP_PARAMETER_ACTIVE))
1213	return stp_get_int_parameter(res1->v, param);
1214    }
1215  return -1;
1216}
1217
1218static int
1219escp2_ink_type(const stp_vars_t *v)
1220{
1221  return escp2_res_param(v, "escp2_ink_type", NULL);
1222}
1223
1224static double
1225escp2_density(const stp_vars_t *v)
1226{
1227  if (stp_check_float_parameter(v, "escp2_density", STP_PARAMETER_ACTIVE))
1228    return stp_get_float_parameter(v, "escp2_density");
1229  else
1230    {
1231      const res_t *res1 = stp_escp2_find_resolution(v);
1232      if (res1->v &&
1233	  stp_check_float_parameter(res1->v, "escp2_density", STP_PARAMETER_ACTIVE))
1234	return stp_get_float_parameter(res1->v, "escp2_density");
1235    }
1236  return 0;
1237}
1238
1239static int
1240escp2_bits(const stp_vars_t *v)
1241{
1242  return escp2_res_param(v, "escp2_bits", NULL);
1243}
1244
1245static int
1246escp2_base_res(const stp_vars_t *v)
1247{
1248  return escp2_res_param(v, "escp2_base_res", NULL);
1249}
1250
1251static int
1252escp2_ink_type_by_res(const stp_vars_t *v, const res_t *res)
1253{
1254  return escp2_res_param(v, "escp2_ink_type", res);
1255}
1256
1257static double
1258escp2_density_by_res(const stp_vars_t *v, const res_t *res)
1259{
1260  if (res)
1261    {
1262      if (res->v &&
1263	  stp_check_float_parameter(res->v, "escp2_density", STP_PARAMETER_ACTIVE))
1264	return stp_get_float_parameter(res->v, "escp2_density");
1265    }
1266  return 0.0;
1267}
1268
1269static int
1270escp2_bits_by_res(const stp_vars_t *v, const res_t *res)
1271{
1272  return escp2_res_param(v, "escp2_bits", res);
1273}
1274
1275static int
1276escp2_base_res_by_res(const stp_vars_t *v, const res_t *res)
1277{
1278  return escp2_res_param(v, "escp2_base_res", res);
1279}
1280
1281static escp2_dropsize_t *
1282escp2_copy_dropsizes(const stp_vars_t *v)
1283{
1284  const res_t *res = stp_escp2_find_resolution(v);
1285  escp2_dropsize_t *ndrops;
1286  if (!res || !(res->v))
1287    return NULL;
1288  ndrops = stp_zalloc(sizeof(escp2_dropsize_t));
1289  if (! ndrops)
1290    return NULL;
1291  if (stp_check_float_parameter(res->v, "DropSize1", STP_PARAMETER_ACTIVE))
1292    {
1293      ndrops->numdropsizes = 1;
1294      ndrops->dropsizes[0] = stp_get_float_parameter(res->v, "DropSize1");
1295    }
1296  if (stp_check_float_parameter(res->v, "DropSize2", STP_PARAMETER_ACTIVE))
1297    {
1298      ndrops->numdropsizes = 2;
1299      ndrops->dropsizes[1] = stp_get_float_parameter(res->v, "DropSize2");
1300    }
1301  if (stp_check_float_parameter(res->v, "DropSize3", STP_PARAMETER_ACTIVE))
1302    {
1303      ndrops->numdropsizes = 3;
1304      ndrops->dropsizes[2] = stp_get_float_parameter(res->v, "DropSize3");
1305    }
1306  return ndrops;
1307}
1308
1309static void
1310escp2_free_dropsizes(escp2_dropsize_t *drops)
1311{
1312  if (drops)
1313    stp_free(drops);
1314}
1315
1316const inklist_t *
1317stp_escp2_inklist(const stp_vars_t *v)
1318{
1319  int i;
1320  const char *ink_list_name = NULL;
1321  const inkgroup_t *inkgroup = escp2_inkgroup(v);
1322
1323  if (stp_check_string_parameter(v, "InkSet", STP_PARAMETER_ACTIVE))
1324    ink_list_name = stp_get_string_parameter(v, "InkSet");
1325  if (ink_list_name)
1326    {
1327      for (i = 0; i < inkgroup->n_inklists; i++)
1328	{
1329	  if (strcmp(ink_list_name, inkgroup->inklists[i].name) == 0)
1330	    return &(inkgroup->inklists[i]);
1331	}
1332    }
1333  STPI_ASSERT(inkgroup, v);
1334  return &(inkgroup->inklists[0]);
1335}
1336
1337static const shade_t *
1338escp2_shades(const stp_vars_t *v, int channel)
1339{
1340  const inklist_t *inklist = stp_escp2_inklist(v);
1341  return &(inklist->shades[channel]);
1342}
1343
1344static shade_t *
1345escp2_copy_shades(const stp_vars_t *v, int channel)
1346{
1347  int i;
1348  shade_t *nshades;
1349  const inklist_t *inklist = stp_escp2_inklist(v);
1350  if (!inklist)
1351    return NULL;
1352  nshades = stp_zalloc(sizeof(shade_t));
1353  nshades->n_shades = inklist->shades[channel].n_shades;
1354  nshades->shades = stp_zalloc(sizeof(double) * inklist->shades[channel].n_shades);
1355  for (i = 0; i < inklist->shades[channel].n_shades; i++)
1356    nshades->shades[i] = inklist->shades[channel].shades[i];
1357  return nshades;
1358}
1359
1360static void
1361escp2_free_shades(shade_t *shades)
1362{
1363  if (shades)
1364    {
1365      if (shades->shades)
1366	stp_free(shades->shades);
1367      stp_free(shades);
1368    }
1369}
1370
1371static const stp_string_list_t *
1372escp2_paperlist(const stp_vars_t *v)
1373{
1374  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1375  return printdef->papers;
1376}
1377
1378static const stp_string_list_t *
1379escp2_slotlist(const stp_vars_t *v)
1380{
1381  stpi_escp2_printer_t *printdef = stp_escp2_get_printer(v);
1382  return printdef->input_slots;
1383}
1384
1385static int
1386supports_borderless(const stp_vars_t *v)
1387{
1388  return (stp_escp2_has_cap(v, MODEL_ZEROMARGIN, MODEL_ZEROMARGIN_YES) ||
1389	  stp_escp2_has_cap(v, MODEL_ZEROMARGIN, MODEL_ZEROMARGIN_FULL) ||
1390	  stp_escp2_has_cap(v, MODEL_ZEROMARGIN, MODEL_ZEROMARGIN_H_ONLY) ||
1391	  stp_escp2_has_cap(v, MODEL_ZEROMARGIN, MODEL_ZEROMARGIN_RESTR));
1392}
1393
1394static int
1395max_nozzle_span(const stp_vars_t *v)
1396{
1397  int nozzle_count = escp2_nozzles(v);
1398  int nozzle_separation = escp2_nozzle_separation(v);
1399  int black_nozzle_count = escp2_black_nozzles(v);
1400  int black_nozzle_separation = escp2_black_nozzle_separation(v);
1401  int nozzle_span = nozzle_count * nozzle_separation;
1402  int black_nozzle_span = black_nozzle_count * black_nozzle_separation;
1403  if (black_nozzle_span > nozzle_span)
1404    return black_nozzle_span;
1405  else
1406    return nozzle_span;
1407}
1408
1409static const stp_raw_t *
1410get_printer_weave(const stp_vars_t *v)
1411{
1412  int i;
1413  const res_t *res = stp_escp2_find_resolution(v);
1414  const printer_weave_list_t *p = escp2_printer_weaves(v);
1415  if (p)
1416    {
1417      const char *name = stp_get_string_parameter(v, "Weave");
1418      int printer_weave_count = p->n_printer_weaves;
1419      if (name)
1420	{
1421	  for (i = 0; i < printer_weave_count; i++)
1422	    {
1423	      if (!strcmp(name, p->printer_weaves[i].name))
1424		return p->printer_weaves[i].command;
1425	    }
1426	}
1427    }
1428  if (res)
1429    return res->command;
1430  return NULL;
1431}
1432
1433static int
1434use_printer_weave(const stp_vars_t *v)
1435{
1436  const res_t *res = stp_escp2_find_resolution(v);
1437  return (!res || res->command);
1438}
1439
1440static void
1441get_resolution_bounds_by_paper_type(const stp_vars_t *v,
1442				    unsigned *max_x, unsigned *max_y,
1443				    unsigned *min_x, unsigned *min_y)
1444{
1445  const paper_t *paper = stp_escp2_get_media_type(v, 1);
1446  *min_x = 0;
1447  *min_y = 0;
1448  *max_x = 0;
1449  *max_y = 0;
1450  if (paper)
1451    {
1452      switch (paper->paper_class)
1453	{
1454	case PAPER_PLAIN:
1455	  *min_x = 0;
1456	  *min_y = 0;
1457	  *max_x = 1440;
1458	  *max_y = 720;
1459	  break;
1460	case PAPER_GOOD:
1461	  *min_x = 360;
1462	  *min_y = 360;
1463	  *max_x = 1440;
1464	  *max_y = 1440;
1465	  break;
1466	case PAPER_PHOTO:
1467	  *min_x = 720;
1468	  *min_y = 360;
1469	  *max_x = 2880;
1470	  *max_y = 1440;
1471	  if (*min_x >= escp2_max_hres(v))
1472	    *min_x = escp2_max_hres(v);
1473	  break;
1474	case PAPER_PREMIUM_PHOTO:
1475	  *min_x = 720;
1476	  *min_y = 720;
1477	  *max_x = 0;
1478	  *max_y = 0;
1479	  if (*min_x >= escp2_max_hres(v))
1480	    *min_x = escp2_max_hres(v);
1481	  break;
1482	case PAPER_TRANSPARENCY:
1483	  *min_x = 360;
1484	  *min_y = 360;
1485	  *max_x = 720;
1486	  *max_y = 720;
1487	  break;
1488	}
1489      stp_dprintf(STP_DBG_ESCP2, v,
1490		  "Paper %s class %d: min_x %d min_y %d max_x %d max_y %d\n",
1491		  paper->text, paper->paper_class, *min_x, *min_y,
1492		  *max_x, *max_y);
1493    }
1494}
1495
1496static int
1497verify_resolution_by_paper_type(const stp_vars_t *v, const res_t *res)
1498{
1499  unsigned min_x = 0;
1500  unsigned min_y = 0;
1501  unsigned max_x = 0;
1502  unsigned max_y = 0;
1503  get_resolution_bounds_by_paper_type(v, &max_x, &max_y, &min_x, &min_y);
1504  if ((max_x == 0 || res->printed_hres <= max_x) &&
1505      (max_y == 0 || res->printed_vres <= max_y) &&
1506      (min_x == 0 || res->printed_hres >= min_x) &&
1507      (min_y == 0 || res->printed_vres >= min_y))
1508    {
1509      stp_dprintf(STP_DBG_ESCP2, v,
1510		  "Resolution %s (%d, %d) GOOD (%d, %d, %d, %d)\n",
1511		  res->name, res->printed_hres, res->printed_vres,
1512		  min_x, min_y, max_x, max_y);
1513      return 1;
1514    }
1515  else
1516    {
1517      stp_dprintf(STP_DBG_ESCP2, v,
1518		  "Resolution %s (%d, %d) BAD (%d, %d, %d, %d)\n",
1519		  res->name, res->printed_hres, res->printed_vres,
1520		  min_x, min_y, max_x, max_y);
1521      return 0;
1522    }
1523}
1524
1525static int
1526verify_resolution(const stp_vars_t *v, const res_t *res)
1527{
1528  int nozzle_width =
1529    (escp2_base_separation(v) / escp2_nozzle_separation(v));
1530  int nozzles = escp2_nozzles(v);
1531  if (escp2_ink_type_by_res(v, res) != -1 &&
1532      res->vres <= escp2_max_vres(v) &&
1533      res->hres <= escp2_max_hres(v) &&
1534      res->vres >= escp2_min_vres(v) &&
1535      res->hres >= escp2_min_hres(v) &&
1536      (nozzles == 1 ||
1537       ((res->vres / nozzle_width) * nozzle_width) == res->vres))
1538    {
1539      int xdpi = res->hres;
1540      int physical_xdpi = escp2_base_res_by_res(v, res);
1541      int horizontal_passes, oversample;
1542      if (physical_xdpi > xdpi)
1543	physical_xdpi = xdpi;
1544      horizontal_passes = xdpi / physical_xdpi;
1545      oversample = horizontal_passes * res->vertical_passes;
1546      if (horizontal_passes < 1)
1547	horizontal_passes = 1;
1548      if (oversample < 1)
1549	oversample = 1;
1550      if (((horizontal_passes * res->vertical_passes) <= STP_MAX_WEAVE) &&
1551	  (res->command || (nozzles > 1 && nozzles > oversample)))
1552	return 1;
1553    }
1554  return 0;
1555}
1556
1557static void
1558get_printer_resolution_bounds(const stp_vars_t *v,
1559			      unsigned *max_x, unsigned *max_y,
1560			      unsigned *min_x, unsigned *min_y)
1561{
1562  int i = 0;
1563  const resolution_list_t *resolutions = escp2_reslist(v);
1564  *max_x = 0;
1565  *max_y = 0;
1566  *min_x = 0;
1567  *min_y = 0;
1568  for (i = 0; i < resolutions->n_resolutions; i++)
1569    {
1570      res_t *res = &(resolutions->resolutions[i]);
1571      if (verify_resolution(v, res))
1572	{
1573	  if (res->printed_hres * res->vertical_passes > *max_x)
1574	    *max_x = res->printed_hres * res->vertical_passes;
1575	  if (res->printed_vres > *max_y)
1576	    *max_y = res->printed_vres;
1577	  if (*min_x == 0 ||
1578	      res->printed_hres * res->vertical_passes < *min_x)
1579	    *min_x = res->printed_hres * res->vertical_passes;
1580	  if (*min_y == 0 || res->printed_vres < *min_y)
1581	    *min_y = res->printed_vres;
1582	}
1583    }
1584  stp_dprintf(STP_DBG_ESCP2, v,
1585	      "Printer bounds: %d %d %d %d\n", *min_x, *min_y, *max_x, *max_y);
1586}
1587
1588static int
1589verify_papersize(const stp_vars_t *v, const stp_papersize_t *pt)
1590{
1591  unsigned int height_limit, width_limit;
1592  unsigned int min_height_limit, min_width_limit;
1593  unsigned int envelope_landscape =
1594    stp_escp2_has_cap(v, MODEL_ENVELOPE_LANDSCAPE, MODEL_ENVELOPE_LANDSCAPE_YES);
1595  width_limit = escp2_max_paper_width(v);
1596  height_limit = escp2_max_paper_height(v);
1597  min_width_limit = escp2_min_paper_width(v);
1598  min_height_limit = escp2_min_paper_height(v);
1599  if (strlen(pt->name) > 0 &&
1600      (pt->paper_size_type != PAPERSIZE_TYPE_ENVELOPE ||
1601       envelope_landscape || pt->height > pt->width) &&
1602      pt->width <= width_limit && pt->height <= height_limit &&
1603      (pt->height >= min_height_limit || pt->height == 0) &&
1604      (pt->width >= min_width_limit || pt->width == 0) &&
1605      (pt->width == 0 || pt->height > 0 ||
1606       stp_escp2_printer_supports_rollfeed(v)))
1607    return 1;
1608  else
1609    return 0;
1610}
1611
1612static int
1613verify_inktype(const stp_vars_t *v, const inkname_t *inks)
1614{
1615  if (inks->inkset == INKSET_EXTENDED)
1616    return 0;
1617  else
1618    return 1;
1619}
1620
1621static const char *
1622get_default_inktype(const stp_vars_t *v)
1623{
1624  const inklist_t *ink_list = stp_escp2_inklist(v);
1625  const paper_t *paper_type;
1626  if (!ink_list)
1627    return NULL;
1628  paper_type = stp_escp2_get_media_type(v, 0);
1629  if (!paper_type)
1630    paper_type = stp_escp2_get_default_media_type(v);
1631  if (paper_type && paper_type->preferred_ink_type)
1632    return paper_type->preferred_ink_type;
1633  else if (stp_escp2_has_cap(v, MODEL_FAST_360, MODEL_FAST_360_YES) &&
1634	   stp_check_string_parameter(v, "Resolution", STP_PARAMETER_ACTIVE))
1635    {
1636      const res_t *res = stp_escp2_find_resolution(v);
1637      if (res)
1638	{
1639	  if (res->vres == 360 && res->hres == escp2_base_res(v))
1640	    {
1641	      int i;
1642	      for (i = 0; i < ink_list->n_inks; i++)
1643		if (strcmp(ink_list->inknames[i].name, "CMYK") == 0)
1644		  return ink_list->inknames[i].name;
1645	    }
1646	}
1647    }
1648  return ink_list->inknames[0].name;
1649}
1650
1651
1652static const inkname_t *
1653get_inktype(const stp_vars_t *v)
1654{
1655  const char	*ink_type = stp_get_string_parameter(v, "InkType");
1656  const inklist_t *ink_list = stp_escp2_inklist(v);
1657  int i;
1658
1659  if (!ink_type || strcmp(ink_type, "None") == 0 ||
1660      (ink_list && ink_list->n_inks == 1))
1661    ink_type = get_default_inktype(v);
1662
1663  if (ink_type && ink_list)
1664    {
1665      for (i = 0; i < ink_list->n_inks; i++)
1666	{
1667	  if (strcmp(ink_type, ink_list->inknames[i].name) == 0)
1668	    return &(ink_list->inknames[i]);
1669	}
1670    }
1671  /*
1672   * If we couldn't find anything, try again with the default ink type.
1673   * This may mean duplicate work, but that's cheap enough.
1674   */
1675  ink_type = get_default_inktype(v);
1676  for (i = 0; i < ink_list->n_inks; i++)
1677    {
1678      if (strcmp(ink_type, ink_list->inknames[i].name) == 0)
1679	return &(ink_list->inknames[i]);
1680    }
1681  /*
1682   * If even *that* doesn't work, try using the first ink type on the list.
1683   */
1684  return &(ink_list->inknames[0]);
1685}
1686
1687static const inkname_t *
1688get_inktype_only(const stp_vars_t *v)
1689{
1690  const char	*ink_type = stp_get_string_parameter(v, "InkType");
1691
1692  if (!ink_type)
1693    return NULL;
1694  else
1695    return get_inktype(v);
1696}
1697
1698static int
1699printer_supports_inkset(const stp_vars_t *v, inkset_id_t inkset)
1700{
1701  const inkgroup_t *ink_group = escp2_inkgroup(v);
1702  int i;
1703  for (i = 0; i < ink_group->n_inklists; i++)
1704    {
1705      const inklist_t *ink_list = &(ink_group->inklists[i]);
1706      if (ink_list)
1707	{
1708	  int j;
1709	  for (j = 0; j < ink_list->n_inks; j++)
1710	    {
1711	      if (ink_list->inknames[j].inkset == inkset)
1712		{
1713		  return 1;
1714		}
1715	    }
1716	}
1717    }
1718  return 0;
1719}
1720
1721static const stp_vars_t *
1722get_media_adjustment(const stp_vars_t *v)
1723{
1724  const paper_t *pt = stp_escp2_get_media_type(v, 0);
1725  if (pt)
1726    return pt->v;
1727  else
1728    return NULL;
1729}
1730
1731/*
1732 * 'escp2_parameters()' - Return the parameter values for the given parameter.
1733 */
1734
1735static stp_parameter_list_t
1736escp2_list_parameters(const stp_vars_t *v)
1737{
1738  stp_parameter_list_t *ret = stp_parameter_list_create();
1739  int i;
1740  for (i = 0; i < the_parameter_count; i++)
1741    stp_parameter_list_add_param(ret, &(the_parameters[i]));
1742  for (i = 0; i < float_parameter_count; i++)
1743    stp_parameter_list_add_param(ret, &(float_parameters[i].param));
1744  for (i = 0; i < int_parameter_count; i++)
1745    stp_parameter_list_add_param(ret, &(int_parameters[i].param));
1746  return ret;
1747}
1748
1749static void
1750set_density_parameter(const stp_vars_t *v,
1751		      stp_parameter_t *description,
1752		      const char *name)
1753{
1754  const inkname_t *ink_name = get_inktype(v);
1755  description->is_active = 0;
1756  if (ink_name && stp_get_string_parameter(v, "PrintingMode") &&
1757      strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
1758    {
1759      int i, j;
1760      for (i = 0; i < ink_name->channel_count; i++)
1761	{
1762	  ink_channel_t *ich = &(ink_name->channels[i]);
1763	  if (ich)
1764	    {
1765	      for (j = 0; j < ich->n_subchannels; j++)
1766		{
1767		  physical_subchannel_t *sch = &(ich->subchannels[j]);
1768		  if (sch && sch->channel_density &&
1769		      !strcmp(name, sch->channel_density))
1770		    {
1771		      description->is_active = 1;
1772		      description->bounds.dbl.lower = 0;
1773		      description->bounds.dbl.upper = 2.0;
1774		      description->deflt.dbl = 1.0;
1775		    }
1776		}
1777	    }
1778	}
1779    }
1780}
1781
1782static void
1783set_hue_map_parameter(const stp_vars_t *v,
1784		      stp_parameter_t *description,
1785		      const char *name)
1786{
1787  const inkname_t *ink_name = get_inktype(v);
1788  description->is_active = 0;
1789  description->deflt.curve = hue_curve_bounds;
1790  description->bounds.curve = stp_curve_create_copy(hue_curve_bounds);
1791  if (ink_name && stp_get_string_parameter(v, "PrintingMode") &&
1792      strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
1793    {
1794      int i;
1795      for (i = 0; i < ink_name->channel_count; i++)
1796	{
1797	  ink_channel_t *ich = &(ink_name->channels[i]);
1798	  if (ich && ich->hue_curve && !strcmp(name, ich->hue_curve_name))
1799	    {
1800	      description->deflt.curve = ich->hue_curve;
1801	      description->is_active = 1;
1802	    }
1803	}
1804    }
1805}
1806
1807static void
1808fill_value_parameters(const stp_vars_t *v,
1809		      stp_parameter_t *description,
1810		      int color)
1811{
1812  const shade_t *shades = escp2_shades(v, color);
1813  const inkname_t *ink_name = get_inktype(v);
1814  description->is_active = 1;
1815  description->bounds.dbl.lower = 0;
1816  description->bounds.dbl.upper = 1.0;
1817  description->deflt.dbl = 1.0;
1818  if (shades && ink_name)
1819    {
1820      const ink_channel_t *channel = &(ink_name->channels[color]);
1821      int i;
1822      for (i = 0; i < channel->n_subchannels; i++)
1823	{
1824	  if (channel->subchannels[i].subchannel_value &&
1825	      strcmp(description->name,
1826		     channel->subchannels[i].subchannel_value) == 0)
1827	    {
1828	      description->deflt.dbl = shades->shades[i];
1829	      return;
1830	    }
1831	}
1832    }
1833}
1834
1835static void
1836set_color_value_parameter(const stp_vars_t *v,
1837			  stp_parameter_t *description,
1838			  int color)
1839{
1840  description->is_active = 0;
1841  if (stp_get_string_parameter(v, "PrintingMode") &&
1842      strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
1843    {
1844      const inkname_t *ink_name = get_inktype(v);
1845      if (ink_name &&
1846	  ink_name->channel_count == 4 &&
1847	  ink_name->channels[color].n_subchannels == 2)
1848	fill_value_parameters(v, description, color);
1849    }
1850}
1851
1852static void
1853set_gray_value_parameter(const stp_vars_t *v,
1854			 stp_parameter_t *description,
1855			 int expected_channels)
1856{
1857  const inkname_t *ink_name = get_inktype_only(v);
1858  description->is_active = 0;
1859  if (!ink_name &&
1860      ((expected_channels == 4 && printer_supports_inkset(v, INKSET_QUADTONE)) ||
1861       (expected_channels == 6 && printer_supports_inkset(v, INKSET_HEXTONE))))
1862    fill_value_parameters(v, description, STP_ECOLOR_K);
1863  else if (ink_name &&
1864      (ink_name->channels[STP_ECOLOR_K].n_subchannels ==
1865       expected_channels))
1866    fill_value_parameters(v, description, STP_ECOLOR_K);
1867  else
1868    set_color_value_parameter(v, description, STP_ECOLOR_K);
1869}
1870
1871static void
1872fill_transition_parameters(const stp_vars_t *v,
1873			   stp_parameter_t *description,
1874			   int color)
1875{
1876  const stp_vars_t *paper_adj = get_media_adjustment(v);
1877  description->is_active = 1;
1878  description->bounds.dbl.lower = 0;
1879  description->bounds.dbl.upper = 1.0;
1880  if (paper_adj && stp_check_float_parameter(paper_adj, "SubchannelCutoff", STP_PARAMETER_ACTIVE))
1881    description->deflt.dbl = stp_get_float_parameter(paper_adj, "SubchannelCutoff");
1882  else
1883    description->deflt.dbl = 1.0;
1884}
1885
1886static void
1887set_color_transition_parameter(const stp_vars_t *v,
1888			       stp_parameter_t *description,
1889			       int color)
1890{
1891  description->is_active = 0;
1892  if (stp_get_string_parameter(v, "PrintingMode") &&
1893      strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
1894    {
1895      const inkname_t *ink_name = get_inktype(v);
1896      if (ink_name &&
1897	  ink_name->channel_count == 4 &&
1898	  ink_name->channels[color].n_subchannels == 2)
1899	fill_transition_parameters(v, description, color);
1900    }
1901}
1902
1903static void
1904set_gray_transition_parameter(const stp_vars_t *v,
1905			      stp_parameter_t *description,
1906			      int expected_channels)
1907{
1908  const inkname_t *ink_name = get_inktype_only(v);
1909  description->is_active = 0;
1910  if (!ink_name &&
1911      ((expected_channels == 4 && printer_supports_inkset(v, INKSET_QUADTONE)) ||
1912       (expected_channels == 6 && printer_supports_inkset(v, INKSET_HEXTONE))))
1913    fill_transition_parameters(v, description, STP_ECOLOR_K);
1914  if (ink_name &&
1915      (ink_name->channels[STP_ECOLOR_K].n_subchannels ==
1916       expected_channels))
1917    fill_transition_parameters(v, description, STP_ECOLOR_K);
1918  else
1919    set_color_transition_parameter(v, description, STP_ECOLOR_K);
1920}
1921
1922static void
1923fill_scale_parameters(stp_parameter_t *description)
1924{
1925  description->is_active = 1;
1926  description->bounds.dbl.lower = 0;
1927  description->bounds.dbl.upper = 5.0;
1928  description->deflt.dbl = 1.0;
1929}
1930
1931static void
1932set_color_scale_parameter(const stp_vars_t *v,
1933			       stp_parameter_t *description,
1934			       int color)
1935{
1936  description->is_active = 0;
1937  if (stp_get_string_parameter(v, "PrintingMode") &&
1938      strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
1939    {
1940      const inkname_t *ink_name = get_inktype(v);
1941      if (ink_name &&
1942	  ink_name->channel_count == 4 &&
1943	  ink_name->channels[color].n_subchannels == 2)
1944	fill_scale_parameters(description);
1945    }
1946}
1947
1948static void
1949set_gray_scale_parameter(const stp_vars_t *v,
1950			      stp_parameter_t *description,
1951			      int expected_channels)
1952{
1953  const inkname_t *ink_name = get_inktype_only(v);
1954  description->is_active = 0;
1955  if (!ink_name &&
1956      ((expected_channels == 4 && printer_supports_inkset(v, INKSET_QUADTONE)) ||
1957       (expected_channels == 6 && printer_supports_inkset(v, INKSET_HEXTONE))))
1958    fill_transition_parameters(v, description, STP_ECOLOR_K);
1959  if (ink_name &&
1960      (ink_name->channels[STP_ECOLOR_K].n_subchannels ==
1961       expected_channels))
1962    fill_scale_parameters(description);
1963  else
1964    set_color_scale_parameter(v, description, STP_ECOLOR_K);
1965}
1966
1967static const res_t *
1968find_default_resolution(const stp_vars_t *v, const quality_t *q, int strict)
1969{
1970  const resolution_list_t *resolutions = escp2_reslist(v);
1971  int i = 0;
1972  stp_dprintf(STP_DBG_ESCP2, v, "Quality %s: min %d %d max %d %d, des %d %d\n",
1973	      q->name, q->min_hres, q->min_vres, q->max_hres, q->max_vres,
1974	      q->desired_hres, q->desired_vres);
1975  if (q->desired_hres < 0 || q->desired_vres < 0)
1976    {
1977      for (i = resolutions->n_resolutions - 1; i >= 0; i--)
1978	{
1979	  const res_t *res = &(resolutions->resolutions[i]);
1980	  stp_dprintf(STP_DBG_ESCP2, v, "  Checking resolution %s %d...\n",
1981		      res->name, i);
1982	  if ((q->max_hres <= 0 || res->printed_hres <= q->max_hres) &&
1983	      (q->max_vres <= 0 || res->printed_vres <= q->max_vres) &&
1984	      q->min_hres <= res->printed_hres &&
1985	      q->min_vres <= res->printed_vres &&
1986	      verify_resolution(v, res) &&
1987	      verify_resolution_by_paper_type(v, res))
1988	    return res;
1989	}
1990    }
1991  if (!strict)
1992    {
1993      unsigned max_x, max_y, min_x, min_y;
1994      unsigned desired_hres = q->desired_hres;
1995      unsigned desired_vres = q->desired_vres;
1996      get_resolution_bounds_by_paper_type(v, &max_x, &max_y, &min_x, &min_y);
1997      stp_dprintf(STP_DBG_ESCP2, v, "  Comparing hres %d to %d, %d\n",
1998		  desired_hres, min_x, max_x);
1999      stp_dprintf(STP_DBG_ESCP2, v, "  Comparing vres %d to %d, %d\n",
2000		  desired_vres, min_y, max_y);
2001      if (max_x > 0 && desired_hres > max_x)
2002	{
2003	  stp_dprintf(STP_DBG_ESCP2, v, "  Decreasing hres from %d to %d\n",
2004		      desired_hres, max_x);
2005	  desired_hres = max_x;
2006	}
2007      else if (desired_hres < min_x)
2008	{
2009	  stp_dprintf(STP_DBG_ESCP2, v, "  Increasing hres from %d to %d\n",
2010		      desired_hres, min_x);
2011	  desired_hres = min_x;
2012	}
2013      if (max_y > 0 && desired_vres > max_y)
2014	{
2015	  stp_dprintf(STP_DBG_ESCP2, v, "  Decreasing vres from %d to %d\n",
2016		      desired_vres, max_y);
2017	  desired_vres = max_y;
2018	}
2019      else if (desired_vres < min_y)
2020	{
2021	  stp_dprintf(STP_DBG_ESCP2, v, "  Increasing vres from %d to %d\n",
2022		      desired_vres, min_y);
2023	  desired_vres = min_y;
2024	}
2025      for (i = 0; i < resolutions->n_resolutions; i++)
2026	{
2027	  res_t *res = &(resolutions->resolutions[i]);
2028	  if (verify_resolution(v, res) &&
2029	      res->printed_vres == desired_vres &&
2030	      res->printed_hres == desired_hres)
2031	    {
2032	      stp_dprintf(STP_DBG_ESCP2, v,
2033			  "  Found desired resolution w/o oversample: %s %d: %d * %d, %d\n",
2034			  res->name, i, res->printed_hres,
2035			  res->vertical_passes, res->printed_vres);
2036	      return res;
2037	    }
2038	}
2039      for (i = 0; i < resolutions->n_resolutions; i++)
2040	{
2041	  res_t *res = &(resolutions->resolutions[i]);
2042	  if (verify_resolution(v, res) &&
2043	      res->printed_vres == desired_vres &&
2044	      res->printed_hres * res->vertical_passes == desired_hres)
2045	    {
2046	      stp_dprintf(STP_DBG_ESCP2, v,
2047			  "  Found desired resolution: %s %d: %d * %d, %d\n",
2048			  res->name, i, res->printed_hres,
2049			  res->vertical_passes, res->printed_vres);
2050	      return res;
2051	    }
2052	}
2053      for (i = 0; i < resolutions->n_resolutions; i++)
2054	{
2055	  res_t *res = &(resolutions->resolutions[i]);
2056	  if (verify_resolution(v, res) &&
2057	      (q->min_vres == 0 || res->printed_vres >= q->min_vres) &&
2058	      (q->max_vres == 0 || res->printed_vres <= q->max_vres) &&
2059	      (q->min_hres == 0 ||
2060	       res->printed_hres * res->vertical_passes >=q->min_hres) &&
2061	      (q->max_hres == 0 ||
2062	       res->printed_hres * res->vertical_passes <= q->max_hres))
2063	    {
2064	      stp_dprintf(STP_DBG_ESCP2, v,
2065			  "  Found acceptable resolution: %s %d: %d * %d, %d\n",
2066			  res->name, i, res->printed_hres,
2067			  res->vertical_passes, res->printed_vres);
2068	      return res;
2069	    }
2070	}
2071    }
2072#if 0
2073  if (!strict)			/* Try again to find a match */
2074    {
2075      for (i = 0; i < resolutions->n_resolutions; i++)
2076	{
2077	  res_t *res = &(resolutions->resolutions[i]);
2078	  if (verify_resolution(v, res) &&
2079	      res->printed_vres >= desired_vres &&
2080	      res->printed_hres * res->vertical_passes >= desired_hres &&
2081	      res->printed_vres <= 2 * desired_vres &&
2082	      res->printed_hres * res->vertical_passes <= 2 * desired_hres)
2083	    return res;
2084	}
2085    }
2086#endif
2087  return NULL;
2088}
2089
2090static int
2091verify_quality(const stp_vars_t *v, const quality_t *q)
2092{
2093  unsigned max_x, max_y, min_x, min_y;
2094  get_printer_resolution_bounds(v, &max_x, &max_y, &min_x, &min_y);
2095  if ((q->max_vres == 0 || min_y <= q->max_vres) &&
2096      (q->min_vres == 0 || max_y >= q->min_vres) &&
2097      (q->max_hres == 0 || min_x <= q->max_hres) &&
2098      (q->min_hres == 0 || max_x >= q->min_hres))
2099    {
2100      stp_dprintf(STP_DBG_ESCP2, v, "Quality %s OK: %d %d %d %d\n",
2101		  q->text, q->min_hres, q->min_vres, q->max_hres, q->max_vres);
2102      return 1;
2103    }
2104  else
2105    {
2106      stp_dprintf(STP_DBG_ESCP2, v, "Quality %s not OK: %d %d %d %d\n",
2107		  q->text, q->min_hres, q->min_vres, q->max_hres, q->max_vres);
2108      return 0;
2109    }
2110}
2111
2112static const res_t *
2113find_resolution_from_quality(const stp_vars_t *v, const char *quality,
2114			     int strict)
2115{
2116  int i;
2117  const quality_list_t *quals = escp2_quality_list(v);
2118  /* This is a rather gross hack... */
2119  if (strcmp(quality, "None") == 0)
2120    quality = "Standard";
2121  for (i = 0; i < quals->n_quals; i++)
2122    {
2123      const quality_t *q = &(quals->qualities[i]);
2124      if (strcmp(quality, q->name) == 0 && verify_quality(v, q))
2125	return find_default_resolution(v, q, strict);
2126    }
2127  return NULL;
2128}
2129
2130static const inkname_t *
2131get_raw_inktype(const stp_vars_t *v)
2132{
2133  if (strcmp(stp_get_string_parameter(v, "InputImageType"), "Raw") == 0)
2134    {
2135      const inklist_t *inks = stp_escp2_inklist(v);
2136      int ninktypes = inks->n_inks;
2137      int i;
2138      const char *channel_name = stp_get_string_parameter(v, "RawChannels");
2139      const channel_count_t *count;
2140      if (!channel_name)
2141	goto none;
2142      count = get_channel_count_by_name(channel_name);
2143      if (!count)
2144	goto none;
2145      for (i = 0; i < ninktypes; i++)
2146	if (inks->inknames[i].inkset == INKSET_EXTENDED &&
2147	    (inks->inknames[i].channel_count == count->count))
2148	  return &(inks->inknames[i]);
2149    }
2150 none:
2151  return get_inktype(v);
2152}
2153
2154static void
2155escp2_parameters(const stp_vars_t *v, const char *name,
2156		 stp_parameter_t *description)
2157{
2158  int		i;
2159  description->p_type = STP_PARAMETER_TYPE_INVALID;
2160  if (name == NULL)
2161    return;
2162
2163  for (i = 0; i < float_parameter_count; i++)
2164    if (strcmp(name, float_parameters[i].param.name) == 0)
2165      {
2166	stp_fill_parameter_settings(description,
2167				     &(float_parameters[i].param));
2168	description->deflt.dbl = float_parameters[i].defval;
2169	description->bounds.dbl.upper = float_parameters[i].max;
2170	description->bounds.dbl.lower = float_parameters[i].min;
2171	break;
2172      }
2173  for (i = 0; i < int_parameter_count; i++)
2174    if (strcmp(name, int_parameters[i].param.name) == 0)
2175      {
2176	stp_fill_parameter_settings(description,
2177				     &(int_parameters[i].param));
2178	description->deflt.integer = int_parameters[i].defval;
2179	description->bounds.integer.upper = int_parameters[i].max;
2180	description->bounds.integer.lower = int_parameters[i].min;
2181	break;
2182      }
2183
2184  for (i = 0; i < the_parameter_count; i++)
2185    if (strcmp(name, the_parameters[i].name) == 0)
2186      {
2187	stp_fill_parameter_settings(description, &(the_parameters[i]));
2188	if (description->p_type == STP_PARAMETER_TYPE_INT)
2189	  {
2190	    description->deflt.integer = 0;
2191	    description->bounds.integer.upper = INT_MAX;
2192	    description->bounds.integer.lower = INT_MIN;
2193	  }
2194	break;
2195      }
2196
2197  description->deflt.str = NULL;
2198  if (strcmp(name, "AutoMode") == 0)
2199    {
2200      description->bounds.str = stp_string_list_create();
2201      stp_string_list_add_string(description->bounds.str, "None",
2202				 _("Full Manual Control"));
2203      stp_string_list_add_string(description->bounds.str, "Auto",
2204				 _("Automatic Setting Control"));
2205      description->deflt.str = "None"; /* so CUPS and Foomatic don't break */
2206    }
2207  else if (strcmp(name, "PageSize") == 0)
2208    {
2209      int papersizes = stp_known_papersizes();
2210      const input_slot_t *slot = stp_escp2_get_input_slot(v);
2211      description->bounds.str = stp_string_list_create();
2212      if (slot && slot->is_cd &&
2213	  !stp_get_boolean_parameter(v, "CDAllowOtherMedia"))
2214	{
2215	  stp_string_list_add_string
2216	    (description->bounds.str, "CD5Inch", _("CD - 5 inch"));
2217	  stp_string_list_add_string
2218	    (description->bounds.str, "CD3Inch", _("CD - 3 inch"));
2219	  stp_string_list_add_string
2220	    (description->bounds.str, "CDCustom", _("CD - Custom"));
2221	}
2222      else
2223	{
2224	  for (i = 0; i < papersizes; i++)
2225	    {
2226	      const stp_papersize_t *pt = stp_get_papersize_by_index(i);
2227	      if (verify_papersize(v, pt))
2228		stp_string_list_add_string(description->bounds.str,
2229					   pt->name, gettext(pt->text));
2230	    }
2231	}
2232      description->deflt.str =
2233	stp_string_list_param(description->bounds.str, 0)->name;
2234    }
2235  else if (strcmp(name, "CDAllowOtherMedia") == 0)
2236    {
2237      const input_slot_t *slot = stp_escp2_get_input_slot(v);
2238      if (stp_escp2_printer_supports_print_to_cd(v) &&
2239	  (!slot || slot->is_cd))
2240	description->is_active = 1;
2241      else
2242	description->is_active = 0;
2243    }
2244  else if (strcmp(name, "CDInnerRadius") == 0 )
2245    {
2246      const input_slot_t *slot = stp_escp2_get_input_slot(v);
2247      description->bounds.str = stp_string_list_create();
2248      if (stp_escp2_printer_supports_print_to_cd(v) &&
2249	  (!slot || slot->is_cd) &&
2250	  (!stp_get_string_parameter(v, "PageSize") ||
2251	   strcmp(stp_get_string_parameter(v, "PageSize"), "CDCustom") != 0))
2252	{
2253	  stp_string_list_add_string
2254	    (description->bounds.str, "None", _("Normal"));
2255	  stp_string_list_add_string
2256	    (description->bounds.str, "Small", _("Print To Hub"));
2257	  description->deflt.str =
2258	    stp_string_list_param(description->bounds.str, 0)->name;
2259	}
2260      else
2261	description->is_active = 0;
2262    }
2263  else if (strcmp(name, "CDInnerDiameter") == 0 )
2264    {
2265      const input_slot_t *slot = stp_escp2_get_input_slot(v);
2266      description->bounds.dimension.lower = 16 * 10 * 72 / 254;
2267      description->bounds.dimension.upper = 43 * 10 * 72 / 254;
2268      description->deflt.dimension = 43 * 10 * 72 / 254;
2269      if (stp_escp2_printer_supports_print_to_cd(v) &&
2270	  (!slot || slot->is_cd) &&
2271	  (!stp_get_string_parameter(v, "PageSize") ||
2272	   strcmp(stp_get_string_parameter(v, "PageSize"), "CDCustom") == 0))
2273	description->is_active = 1;
2274      else
2275	description->is_active = 0;
2276    }
2277  else if (strcmp(name, "CDOuterDiameter") == 0 )
2278    {
2279      const input_slot_t *slot = stp_escp2_get_input_slot(v);
2280      description->bounds.dimension.lower = 65 * 10 * 72 / 254;
2281      description->bounds.dimension.upper = 120 * 10 * 72 / 254;
2282      description->deflt.dimension = 329;
2283      if (stp_escp2_printer_supports_print_to_cd(v) &&
2284	  (!slot || slot->is_cd) &&
2285	  (!stp_get_string_parameter(v, "PageSize") ||
2286	   strcmp(stp_get_string_parameter(v, "PageSize"), "CDCustom") == 0))
2287	description->is_active = 1;
2288      else
2289	description->is_active = 0;
2290    }
2291  else if (strcmp(name, "CDXAdjustment") == 0 ||
2292	   strcmp(name, "CDYAdjustment") == 0)
2293    {
2294      const input_slot_t *slot = stp_escp2_get_input_slot(v);
2295      description->bounds.dimension.lower = -15;
2296      description->bounds.dimension.upper = 15;
2297      description->deflt.dimension = 0;
2298      if (stp_escp2_printer_supports_print_to_cd(v) && (!slot || slot->is_cd))
2299	description->is_active = 1;
2300      else
2301	description->is_active = 0;
2302    }
2303  else if (strcmp(name, "Quality") == 0)
2304    {
2305      const quality_list_t *quals = escp2_quality_list(v);
2306      int has_standard_quality = 0;
2307      description->bounds.str = stp_string_list_create();
2308      stp_string_list_add_string(description->bounds.str, "None",
2309				 _("Manual Control"));
2310      for (i = 0; i < quals->n_quals; i++)
2311	{
2312	  const quality_t *q = &(quals->qualities[i]);
2313	  if (verify_quality(v, q))
2314	    stp_string_list_add_string(description->bounds.str, q->name,
2315				       gettext(q->text));
2316	  if (strcmp(q->name, "Standard") == 0)
2317	    has_standard_quality = 1;
2318	}
2319      if (has_standard_quality)
2320	description->deflt.str = "Standard";
2321      else
2322	description->deflt.str = "None";
2323    }
2324  else if (strcmp(name, "Resolution") == 0)
2325    {
2326      const resolution_list_t *resolutions = escp2_reslist(v);
2327      description->bounds.str = stp_string_list_create();
2328      stp_string_list_add_string(description->bounds.str, "None",
2329				 _("Default"));
2330      description->deflt.str = "None";
2331      for (i = 0; i < resolutions->n_resolutions; i++)
2332	{
2333	  res_t *res = &(resolutions->resolutions[i]);
2334	  if (verify_resolution(v, res))
2335	    stp_string_list_add_string(description->bounds.str,
2336				       res->name, gettext(res->text));
2337	}
2338    }
2339  else if (strcmp(name, "InkType") == 0)
2340    {
2341      const inklist_t *inks = stp_escp2_inklist(v);
2342      int ninktypes = inks->n_inks;
2343      int verified_inktypes = 0;
2344      for (i = 0; i < ninktypes; i++)
2345	if (verify_inktype(v, &(inks->inknames[i])))
2346	  verified_inktypes++;
2347      description->bounds.str = stp_string_list_create();
2348      if (verified_inktypes > 1)
2349	{
2350	  stp_string_list_add_string(description->bounds.str, "None",
2351				     _("Standard"));
2352	  for (i = 0; i < ninktypes; i++)
2353	    if (verify_inktype(v, &(inks->inknames[i])))
2354	      stp_string_list_add_string(description->bounds.str,
2355					 inks->inknames[i].name,
2356					 gettext(inks->inknames[i].text));
2357	  description->deflt.str = "None";
2358	}
2359      else
2360	description->is_active = 0;
2361    }
2362  else if (strcmp(name, "InkSet") == 0)
2363    {
2364      const inkgroup_t *inks = escp2_inkgroup(v);
2365      int ninklists = inks->n_inklists;
2366      description->bounds.str = stp_string_list_create();
2367      if (ninklists > 1)
2368	{
2369	  int has_default_choice = 0;
2370	  for (i = 0; i < ninklists; i++)
2371	    {
2372	      stp_string_list_add_string(description->bounds.str,
2373					 inks->inklists[i].name,
2374					 gettext(inks->inklists[i].text));
2375	      if (strcmp(inks->inklists[i].name, "None") == 0)
2376		has_default_choice = 1;
2377	    }
2378	  description->deflt.str =
2379	    stp_string_list_param(description->bounds.str, 0)->name;
2380	}
2381      else
2382	description->is_active = 0;
2383    }
2384  else if (strcmp(name, "MediaType") == 0)
2385    {
2386      const stp_string_list_t *p = escp2_paperlist(v);
2387      description->is_active = 0;
2388      if (p)
2389	{
2390	  int nmediatypes = stp_string_list_count(p);
2391	  description->bounds.str = stp_string_list_create();
2392	  if (nmediatypes)
2393	    {
2394	      description->is_active = 1;
2395	      for (i = 0; i < nmediatypes; i++)
2396		stp_string_list_add_string(description->bounds.str,
2397					   stp_string_list_param(p, i)->name,
2398					   gettext(stp_string_list_param(p, i)->text));
2399	      description->deflt.str =
2400		stp_string_list_param(description->bounds.str, 0)->name;
2401	    }
2402	}
2403    }
2404  else if (strcmp(name, "InputSlot") == 0)
2405    {
2406      const stp_string_list_t *p = escp2_slotlist(v);
2407      description->is_active = 0;
2408      if (p)
2409	{
2410	  int nslots = stp_string_list_count(p);
2411	  description->bounds.str = stp_string_list_create();
2412	  if (nslots)
2413	    {
2414	      description->is_active = 1;
2415	      for (i = 0; i < nslots; i++)
2416		stp_string_list_add_string(description->bounds.str,
2417					   stp_string_list_param(p, i)->name,
2418					   gettext(stp_string_list_param(p, i)->text));
2419	      description->deflt.str =
2420		stp_string_list_param(description->bounds.str, 0)->name;
2421	    }
2422	}
2423    }
2424  else if (strcmp(name, "PrintingDirection") == 0)
2425    {
2426      description->bounds.str = stp_string_list_create();
2427      stp_string_list_add_string
2428	(description->bounds.str, "None", _("Automatic"));
2429      stp_string_list_add_string
2430	(description->bounds.str, "Bidirectional", _("Bidirectional"));
2431      stp_string_list_add_string
2432	(description->bounds.str, "Unidirectional", _("Unidirectional"));
2433      description->deflt.str =
2434	stp_string_list_param(description->bounds.str, 0)->name;
2435    }
2436  else if (strcmp(name, "Weave") == 0)
2437    {
2438      description->bounds.str = stp_string_list_create();
2439      if (stp_escp2_has_cap(v, MODEL_COMMAND, MODEL_COMMAND_PRO))
2440	{
2441	  const res_t *res = stp_escp2_find_resolution(v);
2442	  const printer_weave_list_t *printer_weaves = escp2_printer_weaves(v);
2443	  int nprinter_weaves = 0;
2444	  if (printer_weaves && use_printer_weave(v) && (!res || res->command))
2445	    nprinter_weaves = printer_weaves->n_printer_weaves;
2446	  if (nprinter_weaves)
2447	    {
2448	      stp_string_list_add_string(description->bounds.str, "None",
2449					 _("Standard"));
2450	      for (i = 0; i < nprinter_weaves; i++)
2451		stp_string_list_add_string(description->bounds.str,
2452					   printer_weaves->printer_weaves[i].name,
2453					   gettext(printer_weaves->printer_weaves[i].text));
2454	    }
2455	  else
2456	    description->is_active = 0;
2457	}
2458      else
2459	{
2460	  stp_string_list_add_string
2461	    (description->bounds.str, "None", _("Standard"));
2462	  stp_string_list_add_string
2463	    (description->bounds.str, "Alternate", _("Alternate Fill"));
2464	  stp_string_list_add_string
2465	    (description->bounds.str, "Ascending", _("Ascending Fill"));
2466	  stp_string_list_add_string
2467	    (description->bounds.str, "Descending", _("Descending Fill"));
2468	  stp_string_list_add_string
2469	    (description->bounds.str, "Ascending2X", _("Ascending Double"));
2470	  stp_string_list_add_string
2471	    (description->bounds.str, "Staggered", _("Nearest Neighbor Avoidance"));
2472	}
2473      if (description->is_active)
2474	description->deflt.str =
2475	  stp_string_list_param(description->bounds.str, 0)->name;
2476    }
2477  else if (strcmp(name, "OutputOrder") == 0)
2478    {
2479      description->bounds.str = stp_string_list_create();
2480      description->deflt.str = "Reverse";
2481    }
2482  else if (strcmp(name, "FullBleed") == 0)
2483    {
2484      const input_slot_t *slot = stp_escp2_get_input_slot(v);
2485      if (slot && slot->is_cd)
2486	description->is_active = 0;
2487      else if (supports_borderless(v))
2488	description->deflt.boolean = 0;
2489      else
2490	description->is_active = 0;
2491    }
2492  else if (strcmp(name, "Duplex") == 0)
2493    {
2494      if (stp_escp2_printer_supports_duplex(v))
2495	{
2496	  const input_slot_t *slot = stp_escp2_get_input_slot(v);
2497	  if (slot && !slot->duplex)
2498	    description->is_active = 0;
2499	  else
2500	    {
2501	      description->bounds.str = stp_string_list_create();
2502	      stp_string_list_add_string
2503		(description->bounds.str, "None", _("Off"));
2504	      stp_string_list_add_string
2505		(description->bounds.str, "DuplexNoTumble", _("Long Edge (Standard)"));
2506	      stp_string_list_add_string
2507		(description->bounds.str, "DuplexTumble", _("Short Edge(Flip)"));
2508	      description->deflt.str = "None";
2509	    }
2510	}
2511      else
2512	description->is_active = 0;
2513    }
2514  else if (strcmp(name, "CyanDensity") == 0 ||
2515	   strcmp(name, "MagentaDensity") == 0 ||
2516	   strcmp(name, "YellowDensity") == 0 ||
2517	   strcmp(name, "BlackDensity") == 0 ||
2518	   strcmp(name, "RedDensity") == 0 ||
2519	   strcmp(name, "BlueDensity") == 0 ||
2520	   strcmp(name, "OrangeDensity") == 0)
2521    set_density_parameter(v, description, name);
2522  else if (strcmp(name, "CyanHueCurve") == 0 ||
2523	   strcmp(name, "MagentaHueCurve") == 0 ||
2524	   strcmp(name, "YellowHueCurve") == 0 ||
2525	   strcmp(name, "RedHueCurve") == 0 ||
2526	   strcmp(name, "BlueHueCurve") == 0 ||
2527	   strcmp(name, "OrangeHueCurve") == 0)
2528    set_hue_map_parameter(v, description, name);
2529  else if (strcmp(name, "UseGloss") == 0)
2530    {
2531      const inkname_t *ink_name = get_inktype(v);
2532      if (ink_name && ink_name->aux_channel_count > 0)
2533	description->is_active = 1;
2534      else
2535	description->is_active = 0;
2536    }
2537  else if (strcmp(name, "GlossLimit") == 0)
2538    {
2539      const inkname_t *ink_name = get_inktype(v);
2540      if (ink_name && ink_name->aux_channel_count > 0)
2541	description->is_active = 1;
2542      else
2543	description->is_active = 0;
2544    }
2545  else if (strcmp(name, "DropSize1") == 0 ||
2546	   strcmp(name, "DropSize2") == 0 ||
2547	   strcmp(name, "DropSize3") == 0)
2548    {
2549      if (stp_escp2_has_cap(v, MODEL_VARIABLE_DOT, MODEL_VARIABLE_YES))
2550	{
2551	  const res_t *res = stp_escp2_find_resolution(v);
2552	  if (res && res->v &&
2553	      stp_check_float_parameter(v, name, STP_PARAMETER_ACTIVE))
2554	    description->deflt.dbl = stp_get_float_parameter(v, name);
2555	  description->is_active = 1;
2556	}
2557      else
2558	description->is_active = 0;
2559    }
2560  else if (strcmp(name, "BlackTrans") == 0 ||
2561	   strcmp(name, "GCRLower") == 0 ||
2562	   strcmp(name, "GCRUpper") == 0)
2563    {
2564      const stp_vars_t *paper_adj = get_media_adjustment(v);
2565      if (paper_adj &&
2566	  stp_get_string_parameter(v, "PrintingMode") &&
2567	  strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
2568	{
2569	  if (paper_adj && stp_check_float_parameter(paper_adj, name, STP_PARAMETER_ACTIVE))
2570	    description->deflt.dbl = stp_get_float_parameter(paper_adj, name);
2571	  else
2572	    description->p_type = STP_PARAMETER_TYPE_INVALID;
2573	}
2574      else
2575	description->p_type = STP_PARAMETER_TYPE_INVALID;
2576    }
2577  else if (strcmp(name, "GrayValue") == 0)
2578    set_gray_value_parameter(v, description, 2);
2579  else if (strcmp(name, "DarkGrayValue") == 0 ||
2580	   strcmp(name, "LightGrayValue") == 0)
2581    set_gray_value_parameter(v, description, 3);
2582  else if (strcmp(name, "Gray1Value") == 0 ||
2583	   strcmp(name, "Gray2Value") == 0 ||
2584	   strcmp(name, "Gray3Value") == 0)
2585    set_gray_value_parameter(v, description, 4);
2586  else if (strcmp(name, "LightCyanValue") == 0)
2587    set_color_value_parameter(v, description, STP_ECOLOR_C);
2588  else if (strcmp(name, "LightMagentaValue") == 0)
2589    set_color_value_parameter(v, description, STP_ECOLOR_M);
2590  else if (strcmp(name, "DarkYellowValue") == 0)
2591    set_color_value_parameter(v, description, STP_ECOLOR_Y);
2592  else if (strcmp(name, "GrayTrans") == 0)
2593    set_gray_transition_parameter(v, description, 2);
2594  else if (strcmp(name, "DarkGrayTrans") == 0 ||
2595	   strcmp(name, "LightGrayTrans") == 0)
2596    set_gray_transition_parameter(v, description, 3);
2597  else if (strcmp(name, "Gray1Trans") == 0 ||
2598	   strcmp(name, "Gray2Trans") == 0 ||
2599	   strcmp(name, "Gray3Trans") == 0)
2600    set_gray_transition_parameter(v, description, 4);
2601  else if (strcmp(name, "LightCyanTrans") == 0)
2602    set_color_transition_parameter(v, description, STP_ECOLOR_C);
2603  else if (strcmp(name, "LightMagentaTrans") == 0)
2604    set_color_transition_parameter(v, description, STP_ECOLOR_M);
2605  else if (strcmp(name, "DarkYellowTrans") == 0)
2606    set_color_transition_parameter(v, description, STP_ECOLOR_Y);
2607  else if (strcmp(name, "GrayScale") == 0)
2608    set_gray_scale_parameter(v, description, 2);
2609  else if (strcmp(name, "DarkGrayScale") == 0 ||
2610	   strcmp(name, "LightGrayScale") == 0)
2611    set_gray_scale_parameter(v, description, 3);
2612  else if (strcmp(name, "Gray1Scale") == 0 ||
2613	   strcmp(name, "Gray2Scale") == 0 ||
2614	   strcmp(name, "Gray3Scale") == 0)
2615    set_gray_scale_parameter(v, description, 4);
2616  else if (strcmp(name, "LightCyanScale") == 0)
2617    set_color_scale_parameter(v, description, STP_ECOLOR_C);
2618  else if (strcmp(name, "LightMagentaScale") == 0)
2619    set_color_scale_parameter(v, description, STP_ECOLOR_M);
2620  else if (strcmp(name, "DarkYellowScale") == 0)
2621    set_color_scale_parameter(v, description, STP_ECOLOR_Y);
2622  else if (strcmp(name, "AlignmentPasses") == 0)
2623    {
2624      description->deflt.integer = escp2_alignment_passes(v);
2625    }
2626  else if (strcmp(name, "AlignmentChoices") == 0)
2627    {
2628      description->deflt.integer = escp2_alignment_choices(v);
2629    }
2630  else if (strcmp(name, "SupportsInkChange") == 0)
2631    {
2632      description->deflt.integer =
2633	stp_escp2_has_cap(v, MODEL_SUPPORTS_INK_CHANGE,
2634		      MODEL_SUPPORTS_INK_CHANGE_YES);
2635    }
2636  else if (strcmp(name, "AlternateAlignmentPasses") == 0)
2637    {
2638      description->deflt.integer = escp2_alternate_alignment_passes(v);
2639    }
2640  else if (strcmp(name, "AlternateAlignmentChoices") == 0)
2641    {
2642      description->deflt.integer = escp2_alternate_alignment_choices(v);
2643    }
2644  else if (strcmp(name, "InkChannels") == 0)
2645    {
2646      description->deflt.integer = escp2_physical_channels(v);
2647    }
2648  else if (strcmp(name, "ChannelNames") == 0)
2649    {
2650      const stp_string_list_t *channel_names = escp2_channel_names(v);
2651      if (channel_names)
2652	{
2653	  description->bounds.str = stp_string_list_create_copy(channel_names);
2654	  description->deflt.str =
2655	    stp_string_list_param(description->bounds.str, 0)->name;
2656	}
2657      else
2658	description->p_type = STP_PARAMETER_TYPE_INVALID;
2659    }
2660  else if (strcmp(name, "SupportsPacketMode") == 0)
2661    {
2662      description->deflt.boolean =
2663	stp_escp2_has_cap(v, MODEL_PACKET_MODE, MODEL_PACKET_MODE_YES);
2664    }
2665  else if (strcmp(name, "PrintingMode") == 0)
2666    {
2667      description->bounds.str = stp_string_list_create();
2668      stp_string_list_add_string
2669	(description->bounds.str, "Color", _("Color"));
2670      stp_string_list_add_string
2671	(description->bounds.str, "BW", _("Black and White"));
2672      description->deflt.str =
2673	stp_string_list_param(description->bounds.str, 0)->name;
2674    }
2675  else if (strcmp(name, "RawChannels") == 0)
2676    {
2677      const inklist_t *inks = stp_escp2_inklist(v);
2678      int ninktypes = inks->n_inks;
2679      description->bounds.str = stp_string_list_create();
2680      if (ninktypes >= 1)
2681	{
2682	  stp_string_list_add_string(description->bounds.str, "None", "None");
2683	  for (i = 0; i < ninktypes; i++)
2684	    if (inks->inknames[i].inkset == INKSET_EXTENDED)
2685	      {
2686		const channel_count_t *ch =
2687		  (get_channel_count_by_number
2688		   (inks->inknames[i].channel_count));
2689		stp_string_list_add_string(description->bounds.str,
2690					   ch->name, ch->name);
2691	      }
2692	  description->deflt.str =
2693	    stp_string_list_param(description->bounds.str, 0)->name;
2694	}
2695      else
2696	description->is_active = 0;
2697    }
2698  else if (strcmp(name, "RawChannelNames") == 0)
2699    {
2700      const inkname_t *ink_name = get_raw_inktype(v);
2701      if (ink_name)
2702	{
2703	  description->bounds.str = stp_string_list_create();
2704	  for (i = 0; i < ink_name->channel_count; i++)
2705	    {
2706	      int j;
2707	      const ink_channel_t *ic = &(ink_name->channels[i]);
2708	      if (ic)
2709		for (j = 0; j < ic->n_subchannels; j++)
2710		  if (ic->subchannels[j].name)
2711		    stp_string_list_add_string(description->bounds.str,
2712					       ic->subchannels[j].name,
2713					       gettext(ic->subchannels[j].text));
2714	    }
2715	  for (i = 0; i < ink_name->aux_channel_count; i++)
2716	    {
2717	      int j;
2718	      const ink_channel_t *ic = &(ink_name->aux_channels[i]);
2719	      if (ic)
2720		for (j = 0; j < ic->n_subchannels; j++)
2721		  if (ic->subchannels[j].name)
2722		    stp_string_list_add_string(description->bounds.str,
2723					       ic->subchannels[j].name,
2724					       gettext(ic->subchannels[j].text));
2725	    }
2726	  description->deflt.str =
2727	    stp_string_list_param(description->bounds.str, 0)->name;
2728	}
2729    }
2730  else if (strcmp(name, "MultiChannelLimit") == 0)
2731    {
2732      description->is_active = 0;
2733      if (stp_get_string_parameter(v, "PrintingMode") &&
2734	  strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
2735	{
2736	  const inkname_t *ink_name = get_inktype(v);
2737	  if (ink_name && ink_name->inkset == INKSET_OTHER)
2738	    description->is_active = 1;
2739	}
2740    }
2741  else if (strcmp(name, "PageDryTime") == 0 ||
2742	   strcmp(name, "ScanDryTime") == 0 ||
2743	   strcmp(name, "ScanMinDryTime") == 0 ||
2744	   strcmp(name, "FeedAdjustment") == 0 ||
2745	   strcmp(name, "PaperThickness") == 0 ||
2746	   strcmp(name, "VacuumIntensity") == 0 ||
2747	   strcmp(name, "FeedSequence") == 0 ||
2748	   strcmp(name, "PrintMethod") == 0 ||
2749	   strcmp(name, "PaperMedia") == 0 ||
2750	   strcmp(name, "PaperMediaSize") == 0 ||
2751	   strcmp(name, "PlatenGap") == 0)
2752    {
2753      description->is_active = 0;
2754      if (stp_escp2_has_media_feature(v, name))
2755	description->is_active = 1;
2756    }
2757  else if (strcmp(name, "BandEnhancement") == 0)
2758    {
2759      description->is_active = 1;
2760    }
2761}
2762
2763const res_t *
2764stp_escp2_find_resolution(const stp_vars_t *v)
2765{
2766  const char *resolution = stp_get_string_parameter(v, "Resolution");
2767  if (resolution)
2768    {
2769      const resolution_list_t *resolutions = escp2_reslist(v);
2770      int i;
2771      for (i = 0; i < resolutions->n_resolutions; i++)
2772	{
2773	  const res_t *res = &(resolutions->resolutions[i]);
2774	  if (!strcmp(resolution, res->name))
2775	    return res;
2776	  else if (!strcmp(res->name, ""))
2777	    return NULL;
2778	}
2779    }
2780  if (stp_check_string_parameter(v, "Quality", STP_PARAMETER_ACTIVE))
2781    {
2782      const res_t *default_res =
2783	find_resolution_from_quality(v, stp_get_string_parameter(v, "Quality"),
2784				     0);
2785      if (default_res)
2786	{
2787	  stp_dprintf(STP_DBG_ESCP2, v,
2788		      "Setting resolution to %s from quality %s\n",
2789		      default_res->name,
2790		      stp_get_string_parameter(v, "Quality"));
2791	  return default_res;
2792	}
2793      else
2794	stp_dprintf(STP_DBG_ESCP2, v, "Unable to map quality %s\n",
2795		    stp_get_string_parameter(v, "Quality"));
2796    }
2797  return NULL;
2798}
2799
2800static inline int
2801imax(int a, int b)
2802{
2803  if (a > b)
2804    return a;
2805  else
2806    return b;
2807}
2808
2809static void
2810escp2_media_size(const stp_vars_t *v,	/* I */
2811		 int  *width,		/* O - Width in points */
2812		 int  *height) 		/* O - Height in points */
2813{
2814  if (stp_get_page_width(v) > 0 && stp_get_page_height(v) > 0)
2815    {
2816      *width = stp_get_page_width(v);
2817      *height = stp_get_page_height(v);
2818    }
2819  else
2820    {
2821      const char *page_size = stp_get_string_parameter(v, "PageSize");
2822      const stp_papersize_t *papersize = NULL;
2823      if (page_size)
2824	papersize = stp_get_papersize_by_name(page_size);
2825      if (!papersize)
2826	{
2827	  *width = 1;
2828	  *height = 1;
2829	}
2830      else
2831	{
2832	  *width = papersize->width;
2833	  *height = papersize->height;
2834	}
2835      if (*width == 0 || *height == 0)
2836	{
2837	  const input_slot_t *slot = stp_escp2_get_input_slot(v);
2838	  if (slot && slot->is_cd)
2839	    {
2840	      papersize = stp_get_papersize_by_name("CDCustom");
2841	      if (papersize)
2842		{
2843		  if (*width == 0)
2844		    *width = papersize->width;
2845		  if (*height == 0)
2846		    *height = papersize->height;
2847		}
2848	    }
2849	  else
2850	    {
2851	      int papersizes = stp_known_papersizes();
2852	      int i;
2853	      for (i = 0; i < papersizes; i++)
2854		{
2855		  papersize = stp_get_papersize_by_index(i);
2856		  if (verify_papersize(v, papersize))
2857		    {
2858		      if (*width == 0)
2859			*width = papersize->width;
2860		      if (*height == 0)
2861			*height = papersize->height;
2862		      break;
2863		    }
2864		}
2865	    }
2866	}
2867      if (*width == 0)
2868	*width = 612;
2869      if (*height == 0)
2870	*height = 792;
2871    }
2872}
2873
2874static void
2875internal_imageable_area(const stp_vars_t *v, int use_paper_margins,
2876			int use_maximum_area,
2877			int *left, int *right, int *bottom, int *top)
2878{
2879  int	width, height;			/* Size of page */
2880  int	rollfeed = 0;			/* Roll feed selected */
2881  int	cd = 0;			/* CD selected */
2882  const char *media_size = stp_get_string_parameter(v, "PageSize");
2883  const char *duplex = stp_get_string_parameter(v, "Duplex");
2884  int left_margin = 0;
2885  int right_margin = 0;
2886  int bottom_margin = 0;
2887  int top_margin = 0;
2888  const stp_papersize_t *pt = NULL;
2889  const input_slot_t *input_slot = NULL;
2890
2891  if (media_size)
2892    pt = stp_get_papersize_by_name(media_size);
2893
2894  input_slot = stp_escp2_get_input_slot(v);
2895  if (input_slot)
2896    {
2897      cd = input_slot->is_cd;
2898      rollfeed = input_slot->is_roll_feed;
2899    }
2900
2901  escp2_media_size(v, &width, &height);
2902  if (cd)
2903    {
2904      if (pt)
2905	{
2906	  left_margin = pt->left;
2907	  right_margin = pt->right;
2908	  bottom_margin = pt->bottom;
2909	  top_margin = pt->top;
2910	}
2911      else
2912	{
2913	  left_margin = 0;
2914	  right_margin = 0;
2915	  bottom_margin = 0;
2916	  top_margin = 0;
2917	}
2918    }
2919  else
2920    {
2921      if (pt && use_paper_margins)
2922	{
2923	  left_margin = pt->left;
2924	  right_margin = pt->right;
2925	  bottom_margin = pt->bottom;
2926	  top_margin = pt->top;
2927	}
2928
2929      left_margin = imax(left_margin, escp2_left_margin(v, rollfeed));
2930      right_margin = imax(right_margin, escp2_right_margin(v, rollfeed));
2931      bottom_margin = imax(bottom_margin, escp2_bottom_margin(v, rollfeed));
2932      top_margin = imax(top_margin, escp2_top_margin(v, rollfeed));
2933    }
2934  if (supports_borderless(v) &&
2935      (use_maximum_area ||
2936       (!cd && stp_get_boolean_parameter(v, "FullBleed"))))
2937    {
2938      if (pt)
2939	{
2940	  if (pt->left <= 0 && pt->right <= 0 && pt->top <= 0 &&
2941	      pt->bottom <= 0)
2942	    {
2943	      if (use_paper_margins)
2944		{
2945		  unsigned width_limit = escp2_max_paper_width(v);
2946		  int offset = escp2_zero_margin_offset(v);
2947		  int margin = escp2_micro_left_margin(v);
2948		  int sep = escp2_base_separation(v);
2949		  int delta = -((offset - margin) * 72 / sep);
2950		  left_margin = delta; /* Allow some overlap if paper isn't */
2951		  right_margin = delta; /* positioned correctly */
2952		  if (width - right_margin - 3 > width_limit)
2953		    right_margin = width - width_limit - 3;
2954		  if (! stp_escp2_has_cap(v, MODEL_ZEROMARGIN,
2955					  MODEL_ZEROMARGIN_H_ONLY))
2956		    {
2957		      top_margin = -7;
2958		      bottom_margin = -7;
2959		    }
2960		}
2961	      else
2962		{
2963		  left_margin = 0;
2964		  right_margin = 0;
2965		  if (! stp_escp2_has_cap(v, MODEL_ZEROMARGIN,
2966					  MODEL_ZEROMARGIN_H_ONLY))
2967		    {
2968		      top_margin = 0;
2969		      bottom_margin = 0;
2970		    }
2971		}
2972	    }
2973	}
2974    }
2975  if (!use_maximum_area && duplex && strcmp(duplex, "None") != 0)
2976    {
2977      left_margin = imax(left_margin, escp2_duplex_left_margin(v));
2978      right_margin = imax(right_margin, escp2_duplex_right_margin(v));
2979      bottom_margin = imax(bottom_margin, escp2_duplex_bottom_margin(v));
2980      top_margin = imax(top_margin, escp2_duplex_top_margin(v));
2981    }
2982
2983  if (width > escp2_max_imageable_width(v))
2984    width = escp2_max_imageable_width(v);
2985  if (height > escp2_max_imageable_height(v))
2986    height = escp2_max_imageable_height(v);
2987  *left =	left_margin;
2988  *right =	width - right_margin;
2989  *top =	top_margin;
2990  *bottom =	height - bottom_margin;
2991}
2992
2993/*
2994 * 'escp2_imageable_area()' - Return the imageable area of the page.
2995 */
2996
2997static void
2998escp2_imageable_area(const stp_vars_t *v,   /* I */
2999		     int  *left,	/* O - Left position in points */
3000		     int  *right,	/* O - Right position in points */
3001		     int  *bottom,	/* O - Bottom position in points */
3002		     int  *top)		/* O - Top position in points */
3003{
3004  internal_imageable_area(v, 1, 0, left, right, bottom, top);
3005}
3006
3007static void
3008escp2_maximum_imageable_area(const stp_vars_t *v,   /* I */
3009			     int  *left,   /* O - Left position in points */
3010			     int  *right,  /* O - Right position in points */
3011			     int  *bottom, /* O - Bottom position in points */
3012			     int  *top)    /* O - Top position in points */
3013{
3014  internal_imageable_area(v, 1, 1, left, right, bottom, top);
3015}
3016
3017static void
3018escp2_limit(const stp_vars_t *v,			/* I */
3019	    int *width, int *height,
3020	    int *min_width, int *min_height)
3021{
3022  *width =	escp2_max_paper_width(v);
3023  *height =	escp2_max_paper_height(v);
3024  *min_width =	escp2_min_paper_width(v);
3025  *min_height =	escp2_min_paper_height(v);
3026}
3027
3028static void
3029escp2_describe_resolution(const stp_vars_t *v, int *x, int *y)
3030{
3031  const res_t *res = stp_escp2_find_resolution(v);
3032  if (res && verify_resolution(v, res))
3033    {
3034      *x = res->printed_hres;
3035      *y = res->printed_vres;
3036      return;
3037    }
3038  *x = -1;
3039  *y = -1;
3040}
3041
3042static const char *
3043escp2_describe_output(const stp_vars_t *v)
3044{
3045  const char *printing_mode = stp_get_string_parameter(v, "PrintingMode");
3046  const char *input_image_type = stp_get_string_parameter(v, "InputImageType");
3047  if (input_image_type && strcmp(input_image_type, "Raw") == 0)
3048    return "Raw";
3049  else if (printing_mode && strcmp(printing_mode, "BW") == 0)
3050    return "Grayscale";
3051  else
3052    {
3053      const inkname_t *ink_type = get_inktype(v);
3054      if (ink_type)
3055	{
3056	  switch (ink_type->inkset)
3057	    {
3058	    case INKSET_QUADTONE:
3059	    case INKSET_HEXTONE:
3060	      return "Grayscale";
3061	    case INKSET_OTHER:
3062	    case INKSET_CMYK:
3063	    case INKSET_CcMmYK:
3064	    case INKSET_CcMmYyK:
3065	    case INKSET_CcMmYKk:
3066	    default:
3067	      if (ink_type->channels[0].n_subchannels > 0)
3068		return "KCMY";
3069	      else
3070		return "CMY";
3071	      break;
3072	    }
3073	}
3074      else
3075	return "CMYK";
3076    }
3077}
3078
3079static int
3080escp2_has_advanced_command_set(const stp_vars_t *v)
3081{
3082  return (stp_escp2_has_cap(v, MODEL_COMMAND, MODEL_COMMAND_PRO) ||
3083	  stp_escp2_has_cap(v, MODEL_COMMAND, MODEL_COMMAND_1999) ||
3084	  stp_escp2_has_cap(v, MODEL_COMMAND, MODEL_COMMAND_2000));
3085}
3086
3087static int
3088escp2_use_extended_commands(const stp_vars_t *v, int use_softweave)
3089{
3090  return (stp_escp2_has_cap(v, MODEL_COMMAND, MODEL_COMMAND_PRO) ||
3091	  (stp_escp2_has_cap(v, MODEL_VARIABLE_DOT, MODEL_VARIABLE_YES) &&
3092	   use_softweave));
3093}
3094
3095static int
3096set_raw_ink_type(stp_vars_t *v)
3097{
3098  const inklist_t *inks = stp_escp2_inklist(v);
3099  int ninktypes = inks->n_inks;
3100  int i;
3101  const char *channel_name = stp_get_string_parameter(v, "RawChannels");
3102  const channel_count_t *count;
3103  if (!channel_name)
3104    return 0;
3105  count = get_channel_count_by_name(channel_name);
3106  if (!count)
3107    return 0;
3108
3109  /*
3110   * If we're using raw printer output, we dummy up the appropriate inkset.
3111   */
3112  for (i = 0; i < ninktypes; i++)
3113    if (inks->inknames[i].inkset == INKSET_EXTENDED &&
3114	(inks->inknames[i].channel_count == count->count))
3115      {
3116	stp_dprintf(STP_DBG_INK, v, "Changing ink type from %s to %s\n",
3117		    stp_get_string_parameter(v, "InkType") ?
3118		    stp_get_string_parameter(v, "InkType") : "NULL",
3119		    inks->inknames[i].name);
3120	stp_set_string_parameter(v, "InkType", inks->inknames[i].name);
3121	stp_set_int_parameter(v, "STPIRawChannels", count->count);
3122	return 1;
3123      }
3124  stp_eprintf
3125    (v, _("This printer does not support raw printer output at depth %d\n"),
3126     count->count);
3127  return 0;
3128}
3129
3130static void
3131adjust_density_and_ink_type(stp_vars_t *v)
3132{
3133  escp2_privdata_t *pd = get_privdata(v);
3134  const stp_vars_t *pv = pd->paper_type->v;
3135  double paper_density = .8;
3136
3137  if (pv && stp_check_float_parameter(pv, "Density", STP_PARAMETER_ACTIVE))
3138    paper_density = stp_get_float_parameter(pv, "Density");
3139
3140  if (!stp_check_float_parameter(v, "Density", STP_PARAMETER_DEFAULTED))
3141    {
3142      stp_set_float_parameter_active(v, "Density", STP_PARAMETER_ACTIVE);
3143      stp_set_float_parameter(v, "Density", 1.0);
3144    }
3145  stp_scale_float_parameter(v, "Density", paper_density * escp2_density(v));
3146  pd->drop_size = escp2_ink_type(v);
3147
3148  if (stp_get_float_parameter(v, "Density") > 1.0)
3149    stp_set_float_parameter(v, "Density", 1.0);
3150}
3151
3152static void
3153adjust_print_quality(stp_vars_t *v)
3154{
3155  escp2_privdata_t *pd = get_privdata(v);
3156  const stp_vars_t *pv = pd->paper_type->v;
3157  double k_upper = 1.0;
3158  double k_lower = 0;
3159  double k_transition = 1.0;
3160
3161  /*
3162   * Compute the LUT.  For now, it's 8 bit, but that may eventually
3163   * sometimes change.
3164   */
3165
3166  if (pv)
3167    {
3168      int i;
3169      stp_string_list_t *slist;
3170      stp_set_default_float_parameter(v, "BlackDensity", 1.0);
3171      stp_set_default_float_parameter(v, "Saturation", 1.0);
3172      stp_set_default_float_parameter(v, "Gamma", 1.0);
3173      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_STRING_LIST);
3174      if (slist)
3175	{
3176	  int len = stp_string_list_count(slist);
3177	  for (i = 0; i < len; i++)
3178	    {
3179	      const char *name = stp_string_list_param(slist, i)->name;
3180	      if (!stp_check_string_parameter(v, name, STP_PARAMETER_ACTIVE))
3181		stp_set_string_parameter(v, name,
3182					 stp_get_string_parameter(pv, name));
3183	    }
3184	  stp_string_list_destroy(slist);
3185	}
3186      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_FILE);
3187      if (slist)
3188	{
3189	  int len = stp_string_list_count(slist);
3190	  for (i = 0; i < len; i++)
3191	    {
3192	      const char *name = stp_string_list_param(slist, i)->name;
3193	      if (!stp_check_file_parameter(v, name, STP_PARAMETER_ACTIVE))
3194		stp_set_file_parameter(v, name,
3195				       stp_get_file_parameter(pv, name));
3196	    }
3197	  stp_string_list_destroy(slist);
3198	}
3199      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_INT);
3200      if (slist)
3201	{
3202	  int len = stp_string_list_count(slist);
3203	  for (i = 0; i < len; i++)
3204	    {
3205	      const char *name = stp_string_list_param(slist, i)->name;
3206	      if (!stp_check_int_parameter(v, name, STP_PARAMETER_ACTIVE))
3207		stp_set_int_parameter(v, name,
3208				      stp_get_int_parameter(pv, name));
3209	    }
3210	  stp_string_list_destroy(slist);
3211	}
3212      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_DIMENSION);
3213      if (slist)
3214	{
3215	  int len = stp_string_list_count(slist);
3216	  for (i = 0; i < len; i++)
3217	    {
3218	      const char *name = stp_string_list_param(slist, i)->name;
3219	      if (!stp_check_dimension_parameter(v, name, STP_PARAMETER_ACTIVE))
3220		stp_set_dimension_parameter(v, name,
3221					    stp_get_dimension_parameter(pv, name));
3222	    }
3223	  stp_string_list_destroy(slist);
3224	}
3225      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_BOOLEAN);
3226      if (slist)
3227	{
3228	  int len = stp_string_list_count(slist);
3229	  for (i = 0; i < len; i++)
3230	    {
3231	      const char *name = stp_string_list_param(slist, i)->name;
3232	      if (!stp_check_boolean_parameter(v, name, STP_PARAMETER_ACTIVE))
3233		stp_set_boolean_parameter(v, name,
3234					  stp_get_boolean_parameter(pv, name));
3235	    }
3236	  stp_string_list_destroy(slist);
3237	}
3238      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_CURVE);
3239      if (slist)
3240	{
3241	  int len = stp_string_list_count(slist);
3242	  for (i = 0; i < len; i++)
3243	    {
3244	      const char *name = stp_string_list_param(slist, i)->name;
3245	      if (!stp_check_curve_parameter(v, name, STP_PARAMETER_ACTIVE))
3246		stp_set_curve_parameter(v, name,
3247					stp_get_curve_parameter(pv, name));
3248	    }
3249	  stp_string_list_destroy(slist);
3250	}
3251      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_ARRAY);
3252      if (slist)
3253	{
3254	  int len = stp_string_list_count(slist);
3255	  for (i = 0; i < len; i++)
3256	    {
3257	      const char *name = stp_string_list_param(slist, i)->name;
3258	      if (!stp_check_array_parameter(v, name, STP_PARAMETER_ACTIVE))
3259		stp_set_array_parameter(v, name,
3260					stp_get_array_parameter(pv, name));
3261	    }
3262	  stp_string_list_destroy(slist);
3263	}
3264      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_RAW);
3265      if (slist)
3266	{
3267	  int len = stp_string_list_count(slist);
3268	  for (i = 0; i < len; i++)
3269	    {
3270	      const char *name = stp_string_list_param(slist, i)->name;
3271	      if (!stp_check_raw_parameter(v, name, STP_PARAMETER_ACTIVE))
3272		{
3273		  const stp_raw_t *r = stp_get_raw_parameter(pv, name);
3274		  stp_set_raw_parameter(v, name, r->data, r->bytes);
3275		}
3276	    }
3277	  stp_string_list_destroy(slist);
3278	}
3279      slist = stp_list_parameters(pv, STP_PARAMETER_TYPE_DOUBLE);
3280      if (slist)
3281	{
3282	  int len = stp_string_list_count(slist);
3283	  for (i = 0; i < len; i++)
3284	    {
3285	      const char *name = stp_string_list_param(slist, i)->name;
3286	      if (strcmp(name, "BlackDensity") == 0 ||
3287		  strcmp(name, "Saturation") == 0 ||
3288		  strcmp(name, "Gamma") == 0)
3289		stp_scale_float_parameter(v, name,
3290					  stp_get_float_parameter(pv, name));
3291	      else if (strcmp(name, "GCRLower") == 0)
3292		k_lower = stp_get_float_parameter(pv, "GCRLower");
3293	      else if (strcmp(name, "GCRUpper") == 0)
3294		k_upper = stp_get_float_parameter(pv, "GCRUpper");
3295	      else if (strcmp(name, "BlackTrans") == 0)
3296		k_transition = stp_get_float_parameter(pv, "BlackTrans");
3297	      else if (!stp_check_float_parameter(v, name, STP_PARAMETER_ACTIVE))
3298		stp_set_float_parameter(v, name,
3299					stp_get_float_parameter(pv, name));
3300	    }
3301	  stp_string_list_destroy(slist);
3302	}
3303    }
3304
3305  if (!stp_check_float_parameter(v, "GCRLower", STP_PARAMETER_ACTIVE))
3306    stp_set_default_float_parameter(v, "GCRLower", k_lower);
3307  if (!stp_check_float_parameter(v, "GCRUpper", STP_PARAMETER_ACTIVE))
3308    stp_set_default_float_parameter(v, "GCRUpper", k_upper);
3309  if (!stp_check_float_parameter(v, "BlackTrans", STP_PARAMETER_ACTIVE))
3310    stp_set_default_float_parameter(v, "BlackTrans", k_transition);
3311
3312
3313}
3314
3315static int
3316count_channels(const inkname_t *inks, int use_aux_channels)
3317{
3318  int answer = 0;
3319  int i;
3320  for (i = 0; i < inks->channel_count; i++)
3321    if (inks->channels[i].n_subchannels > 0)
3322      answer += inks->channels[i].n_subchannels;
3323  if (use_aux_channels)
3324    for (i = 0; i < inks->aux_channel_count; i++)
3325      if (inks->aux_channels[i].n_subchannels > 0)
3326	answer += inks->aux_channels[i].n_subchannels;
3327  return answer;
3328}
3329
3330static int
3331compute_channel_count(const inkname_t *ink_type, int channel_limit,
3332		      int use_aux_channels)
3333{
3334  int i;
3335  int physical_channels = 0;
3336  for (i = 0; i < channel_limit; i++)
3337    {
3338      const ink_channel_t *channel = &(ink_type->channels[i]);
3339      if (channel)
3340	physical_channels += channel->n_subchannels;
3341    }
3342  if (use_aux_channels)
3343    for (i = 0; i < ink_type->aux_channel_count; i++)
3344      if (ink_type->aux_channels[i].n_subchannels > 0)
3345	physical_channels += ink_type->aux_channels[i].n_subchannels;
3346  return physical_channels;
3347}
3348
3349static double
3350get_double_param(const stp_vars_t *v, const char *param)
3351{
3352  if (param && stp_check_float_parameter(v, param, STP_PARAMETER_ACTIVE))
3353    return stp_get_float_parameter(v, param);
3354  else
3355    return 1.0;
3356}
3357
3358static void
3359setup_inks(stp_vars_t *v)
3360{
3361  escp2_privdata_t *pd = get_privdata(v);
3362  int i, j;
3363  escp2_dropsize_t *drops;
3364  const inkname_t *ink_type = pd->inkname;
3365  const stp_vars_t *pv = pd->paper_type->v;
3366  int gloss_channel = -1;
3367  double gloss_scale = get_double_param(v, "Density");
3368
3369  drops = escp2_copy_dropsizes(v);
3370  stp_init_debug_messages(v);
3371  if (stp_check_float_parameter(v, "DropSize1", STP_PARAMETER_ACTIVE))
3372    {
3373      drops->dropsizes[0] = stp_get_float_parameter(v, "DropSize1");
3374      if (drops->dropsizes[0] > 0 && drops->numdropsizes < 1)
3375	drops->numdropsizes = 1;
3376    }
3377  if (stp_check_float_parameter(v, "DropSize2", STP_PARAMETER_ACTIVE))
3378    {
3379      drops->dropsizes[1] = stp_get_float_parameter(v, "DropSize2");
3380      if (drops->dropsizes[1] > 0 && drops->numdropsizes < 2)
3381	drops->numdropsizes = 2;
3382    }
3383  if (stp_check_float_parameter(v, "DropSize3", STP_PARAMETER_ACTIVE))
3384    {
3385      drops->dropsizes[2] = stp_get_float_parameter(v, "DropSize3");
3386      if (drops->dropsizes[2] > 0 && drops->numdropsizes < 3)
3387	drops->numdropsizes = 3;
3388    }
3389  for (i = drops->numdropsizes - 1; i >= 0; i--)
3390    {
3391      if (drops->dropsizes[i] > 0)
3392	break;
3393      drops->numdropsizes--;
3394    }
3395  for (i = 0; i < pd->logical_channels; i++)
3396    {
3397      const ink_channel_t *channel = &(ink_type->channels[i]);
3398      if (channel && channel->n_subchannels > 0)
3399	{
3400	  int hue_curve_found = 0;
3401	  const char *param = channel->subchannels[0].channel_density;
3402	  shade_t *shades = escp2_copy_shades(v, i);
3403	  double userval = get_double_param(v, param);
3404	  STPI_ASSERT(shades->n_shades >= channel->n_subchannels, v);
3405	  if (ink_type->inkset != INKSET_EXTENDED)
3406	    {
3407	      if (strcmp(param, "BlackDensity") == 0)
3408		stp_channel_set_black_channel(v, i);
3409	      else if (strcmp(param, "GlossDensity") == 0)
3410		{
3411		  gloss_scale *= get_double_param(v, param);
3412		  gloss_channel = i;
3413		}
3414	    }
3415	  for (j = 0; j < channel->n_subchannels; j++)
3416	    {
3417	      const char *subparam =
3418		channel->subchannels[j].subchannel_value;
3419	      if (subparam &&
3420		  stp_check_float_parameter(v, subparam, STP_PARAMETER_ACTIVE))
3421		shades->shades[j] = stp_get_float_parameter(v, subparam);
3422	    }
3423	  stp_dither_set_inks(v, i, 1.0, ink_darknesses[i % 8],
3424			      channel->n_subchannels, shades->shades,
3425			      drops->numdropsizes, drops->dropsizes);
3426	  for (j = 0; j < channel->n_subchannels; j++)
3427	    {
3428	      const char *subparam =
3429		channel->subchannels[j].subchannel_scale;
3430	      double scale = userval * get_double_param(v, subparam);
3431	      scale *= get_double_param(v, "Density");
3432	      stp_channel_set_density_adjustment(v, i, j, scale);
3433	      subparam =
3434		channel->subchannels[j].subchannel_transition;
3435	      if (subparam &&
3436		  stp_check_float_parameter(v, subparam, STP_PARAMETER_ACTIVE))
3437		stp_channel_set_cutoff_adjustment
3438		  (v, i, j, stp_get_float_parameter(v, subparam));
3439	      else if (pv)
3440		{
3441		  if (subparam &&
3442		      stp_check_float_parameter(pv, subparam, STP_PARAMETER_ACTIVE))
3443		    stp_channel_set_cutoff_adjustment
3444		      (v, i, j, stp_get_float_parameter(pv, subparam));
3445		  else if (stp_check_float_parameter(pv, "SubchannelCutoff", STP_PARAMETER_ACTIVE))
3446		    stp_channel_set_cutoff_adjustment
3447		      (v, i, j, stp_get_float_parameter(pv, "SubchannelCutoff"));
3448		}
3449	    }
3450	  if (ink_type->inkset != INKSET_EXTENDED)
3451	    {
3452	      if (channel->hue_curve_name)
3453		{
3454		  const stp_curve_t *curve = NULL;
3455		  curve = stp_get_curve_parameter(v, channel->hue_curve_name);
3456		  if (curve)
3457		    {
3458		      stp_channel_set_curve(v, i, curve);
3459		      hue_curve_found = 1;
3460		    }
3461		}
3462	      if (channel->hue_curve && !hue_curve_found)
3463		stp_channel_set_curve(v, i, channel->hue_curve);
3464	    }
3465	  escp2_free_shades(shades);
3466	}
3467    }
3468  if (pd->use_aux_channels)
3469    {
3470      int base_count = pd->logical_channels;
3471      for (i = 0; i < ink_type->aux_channel_count; i++)
3472	{
3473	  const ink_channel_t *channel = &(ink_type->aux_channels[i]);
3474	  if (channel && channel->n_subchannels > 0)
3475	    {
3476	      int ch = i + base_count;
3477	      const char *param = channel->subchannels[0].channel_density;
3478	      shade_t *shades = escp2_copy_shades(v, ch);
3479	      double userval = get_double_param(v, param);
3480	      STPI_ASSERT(shades->n_shades >= channel->n_subchannels, v);
3481	      if (strcmp(param, "GlossDensity") == 0)
3482		{
3483		  gloss_scale *= get_double_param(v, param);
3484		  stp_channel_set_gloss_channel(v, ch);
3485		  stp_channel_set_gloss_limit(v, gloss_scale);
3486		}
3487	      for (j = 0; j < channel->n_subchannels; j++)
3488		{
3489		  const char *subparam =
3490		    channel->subchannels[j].subchannel_value;
3491		  if (subparam &&
3492		      stp_check_float_parameter(v, subparam, STP_PARAMETER_ACTIVE))
3493		    shades->shades[j] = stp_get_float_parameter(v, subparam);
3494		}
3495	      stp_dither_set_inks(v, ch, 1.0, ink_darknesses[ch % 8],
3496				  channel->n_subchannels, shades->shades,
3497				  drops->numdropsizes, drops->dropsizes);
3498	      for (j = 0; j < channel->n_subchannels; j++)
3499		{
3500		  const char *subparam =
3501		    channel->subchannels[j].subchannel_scale;
3502		  double scale = userval * get_double_param(v, subparam);
3503		  scale *= get_double_param(v, "Density");
3504		  stp_channel_set_density_adjustment(v, ch, j, scale);
3505		  subparam =
3506		    channel->subchannels[j].subchannel_transition;
3507		  if (subparam &&
3508		      stp_check_float_parameter(v, subparam, STP_PARAMETER_ACTIVE))
3509		    stp_channel_set_cutoff_adjustment
3510		      (v, ch, j, stp_get_float_parameter(v, subparam));
3511		  else if (pv)
3512		    {
3513		      if (subparam &&
3514			  stp_check_float_parameter(pv, subparam, STP_PARAMETER_ACTIVE))
3515			stp_channel_set_cutoff_adjustment
3516			  (v, ch, j, stp_get_float_parameter(pv, subparam));
3517		      else if (stp_check_float_parameter(pv, "SubchannelCutoff", STP_PARAMETER_ACTIVE))
3518			stp_channel_set_cutoff_adjustment
3519			  (v, ch, j, stp_get_float_parameter(pv, "SubchannelCutoff"));
3520		    }
3521		}
3522	      if (channel->hue_curve)
3523		{
3524		  stp_curve_t *curve_tmp =
3525		    stp_curve_create_copy(channel->hue_curve);
3526		  (void) stp_curve_rescale(curve_tmp,
3527					   sqrt(1.0 / stp_get_float_parameter(v, "Gamma")),
3528					   STP_CURVE_COMPOSE_EXPONENTIATE,
3529					   STP_CURVE_BOUNDS_RESCALE);
3530		  stp_channel_set_curve(v, ch, curve_tmp);
3531		  stp_curve_destroy(curve_tmp);
3532		}
3533	      escp2_free_shades(shades);
3534	    }
3535	}
3536    }
3537  escp2_free_dropsizes(drops);
3538  stp_flush_debug_messages(v);
3539}
3540
3541static void
3542setup_head_offset(stp_vars_t *v)
3543{
3544  escp2_privdata_t *pd = get_privdata(v);
3545  int i;
3546  int channel_id = 0;
3547  int channel_limit = pd->logical_channels;
3548  const inkname_t *ink_type = pd->inkname;
3549  if (pd->channels_in_use > pd->logical_channels)
3550    channel_limit = pd->channels_in_use;
3551  pd->head_offset = stp_zalloc(sizeof(int) * channel_limit);
3552  for (i = 0; i < pd->logical_channels; i++)
3553    {
3554      const ink_channel_t *channel = &(ink_type->channels[i]);
3555      if (channel)
3556	{
3557	  int j;
3558	  for (j = 0; j < channel->n_subchannels; j++)
3559	    {
3560	      pd->head_offset[channel_id] =
3561		channel->subchannels[j].head_offset;
3562	      channel_id++;
3563	    }
3564	}
3565    }
3566  if (pd->use_aux_channels)
3567    {
3568      for (i = 0; i < ink_type->aux_channel_count; i++)
3569	{
3570	  const ink_channel_t *channel = &(ink_type->aux_channels[i]);
3571	  if (channel)
3572	    {
3573	      int j;
3574	      for (j = 0; j < channel->n_subchannels; j++)
3575		{
3576		  pd->head_offset[channel_id] =
3577		    channel->subchannels[j].head_offset;
3578		  channel_id++;
3579		}
3580	    }
3581	}
3582    }
3583  if (pd->physical_channels == 1)
3584    pd->head_offset[0] = 0;
3585  pd->max_head_offset = 0;
3586  if (pd->physical_channels > 1)
3587    for (i = 0; i < pd->channels_in_use; i++)
3588      {
3589	pd->head_offset[i] = pd->head_offset[i] * pd->res->vres /
3590	  escp2_base_separation(v);
3591	if (pd->head_offset[i] > pd->max_head_offset)
3592	  pd->max_head_offset = pd->head_offset[i];
3593      }
3594}
3595
3596static int
3597supports_split_channels(stp_vars_t *v)
3598{
3599  escp2_privdata_t *pd = get_privdata(v);
3600  int i;
3601  int split_channel_count = -1;
3602  for (i = 0; i < pd->logical_channels; i++)
3603    {
3604      int j;
3605      if (pd->inkname->channels[i].n_subchannels > 0)
3606	{
3607	  for (j = 0; j < pd->inkname->channels[i].n_subchannels; j++)
3608	    {
3609	      int split_count = pd->inkname->channels[i].subchannels[j].split_channel_count;
3610	      if (split_count == 0)
3611		return 0;
3612	      else if (split_channel_count >= 0 && split_count != split_channel_count)
3613		return 0;
3614	      else
3615		split_channel_count = split_count;
3616	    }
3617	}
3618    }
3619  if (split_channel_count > 0)
3620    return split_channel_count;
3621  else
3622    return 0;
3623}
3624
3625
3626static void
3627setup_split_channels(stp_vars_t *v)
3628{
3629  escp2_privdata_t *pd = get_privdata(v);
3630  /*
3631   * Set up the output channels
3632   */
3633  pd->split_channel_count = supports_split_channels(v);
3634  if (pd->split_channel_count)
3635    {
3636      if (pd->res->vres <
3637	  (escp2_base_separation(v) / escp2_black_nozzle_separation(v)))
3638	{
3639	  int incr =
3640	    (escp2_base_separation(v) / escp2_black_nozzle_separation(v)) /
3641	    pd->res->vres;
3642	  pd->split_channel_count /= incr;
3643	  pd->nozzle_separation *= incr;
3644	  pd->nozzles /= incr;
3645	  pd->min_nozzles /= incr;
3646	}
3647    }
3648}
3649
3650static void
3651setup_basic(stp_vars_t *v)
3652{
3653  escp2_privdata_t *pd = get_privdata(v);
3654  pd->advanced_command_set = escp2_has_advanced_command_set(v);
3655  pd->command_set = stp_escp2_get_cap(v, MODEL_COMMAND);
3656  pd->variable_dots = stp_escp2_has_cap(v, MODEL_VARIABLE_DOT, MODEL_VARIABLE_YES);
3657  pd->has_graymode = stp_escp2_has_cap(v, MODEL_GRAYMODE, MODEL_GRAYMODE_YES);
3658  pd->preinit_sequence = escp2_preinit_sequence(v);
3659  pd->preinit_remote_sequence = escp2_preinit_remote_sequence(v);
3660  pd->deinit_remote_sequence = escp2_postinit_remote_sequence(v);
3661  pd->borderless_sequence = escp2_vertical_borderless_sequence(v);
3662  pd->base_separation = escp2_base_separation(v);
3663  pd->resolution_scale = escp2_resolution_scale(v);
3664}
3665
3666static void
3667setup_misc(stp_vars_t *v)
3668{
3669  escp2_privdata_t *pd = get_privdata(v);
3670  pd->input_slot = stp_escp2_get_input_slot(v);
3671  pd->paper_type = stp_escp2_get_media_type(v, 0);
3672  pd->ink_group = escp2_inkgroup(v);
3673  pd->media_settings = stp_vars_create_copy(pd->paper_type->v);
3674  stp_escp2_set_media_size(pd->media_settings, v);
3675  if (stp_check_float_parameter(v, "PageDryTime", STP_PARAMETER_ACTIVE))
3676    stp_set_float_parameter(pd->media_settings, "PageDryTime",
3677			    stp_get_float_parameter(v, "PageDryTime"));
3678  if (stp_check_float_parameter(v, "ScanDryTime", STP_PARAMETER_ACTIVE))
3679    stp_set_float_parameter(pd->media_settings, "ScanDryTime",
3680			    stp_get_float_parameter(v, "ScanDryTime"));
3681  if (stp_check_float_parameter(v, "ScanMinDryTime", STP_PARAMETER_ACTIVE))
3682    stp_set_float_parameter(pd->media_settings, "ScanMinDryTime",
3683			    stp_get_float_parameter(v, "ScanMinDryTime"));
3684  if (stp_check_int_parameter(v, "FeedAdjustment", STP_PARAMETER_ACTIVE))
3685    stp_set_int_parameter(pd->media_settings, "FeedAdjustment",
3686			  stp_get_int_parameter(v, "FeedAdjustment"));
3687  if (stp_check_int_parameter(v, "PaperThickness", STP_PARAMETER_ACTIVE))
3688    stp_set_int_parameter(pd->media_settings, "PaperThickness",
3689			  stp_get_int_parameter(v, "PaperThickness"));
3690  if (stp_check_int_parameter(v, "VacuumIntensity", STP_PARAMETER_ACTIVE))
3691    stp_set_int_parameter(pd->media_settings, "VacuumIntensity",
3692			  stp_get_int_parameter(v, "VacuumIntensity"));
3693  if (stp_check_int_parameter(v, "FeedSequence", STP_PARAMETER_ACTIVE))
3694    stp_set_int_parameter(pd->media_settings, "FeedSequence",
3695			  stp_get_int_parameter(v, "FeedSequence"));
3696  if (stp_check_int_parameter(v, "PrintMethod", STP_PARAMETER_ACTIVE))
3697    stp_set_int_parameter(pd->media_settings, "PrintMethod",
3698			  stp_get_int_parameter(v, "PrintMethod"));
3699  if (stp_check_int_parameter(v, "PlatenGap", STP_PARAMETER_ACTIVE))
3700    stp_set_int_parameter(pd->media_settings, "PlatenGap",
3701			  stp_get_int_parameter(v, "PlatenGap"));
3702}
3703
3704static void
3705allocate_channels(stp_vars_t *v, int line_length)
3706{
3707  escp2_privdata_t *pd = get_privdata(v);
3708  const inkname_t *ink_type = pd->inkname;
3709  int i, j, k;
3710  int channel_id = 0;
3711  int split_id = 0;
3712
3713  pd->cols = stp_zalloc(sizeof(unsigned char *) * pd->channels_in_use);
3714  pd->channels =
3715    stp_zalloc(sizeof(physical_subchannel_t *) * pd->channels_in_use);
3716  if (pd->split_channel_count)
3717    pd->split_channels = stp_zalloc(sizeof(short) * pd->channels_in_use *
3718				    pd->split_channel_count);
3719
3720  for (i = 0; i < pd->logical_channels; i++)
3721    {
3722      const ink_channel_t *channel = &(ink_type->channels[i]);
3723      if (channel)
3724	{
3725	  for (j = 0; j < channel->n_subchannels; j++)
3726	    {
3727	      const physical_subchannel_t *sc = &(channel->subchannels[j]);
3728	      pd->cols[channel_id] = stp_zalloc(line_length);
3729	      pd->channels[channel_id] = sc;
3730	      stp_dither_add_channel(v, pd->cols[channel_id], i, j);
3731	      if (pd->split_channel_count)
3732		{
3733		  for (k = 0; k < pd->split_channel_count; k++)
3734		    pd->split_channels[split_id++] = sc->split_channels[k];
3735		}
3736	      channel_id++;
3737	    }
3738	}
3739    }
3740  if (pd->use_aux_channels && ink_type->aux_channel_count > 0)
3741    {
3742      for (i = 0; i < ink_type->aux_channel_count; i++)
3743	{
3744	  const ink_channel_t *channel = &(ink_type->aux_channels[i]);
3745	  for (j = 0; j < channel->n_subchannels; j++)
3746	    {
3747	      const physical_subchannel_t *sc = &(channel->subchannels[j]);
3748	      pd->cols[channel_id] = stp_zalloc(line_length);
3749	      pd->channels[channel_id] = sc;
3750	      stp_dither_add_channel(v, pd->cols[channel_id],
3751				     i + pd->logical_channels, j);
3752	      if (pd->split_channel_count)
3753		{
3754		  for (k = 0; k < pd->split_channel_count; k++)
3755		    pd->split_channels[split_id++] = sc->split_channels[k];
3756		}
3757	      channel_id++;
3758	    }
3759	}
3760    }
3761  stp_set_string_parameter(v, "STPIOutputType", escp2_describe_output(v));
3762}
3763
3764static unsigned
3765gcd(unsigned a, unsigned b)
3766{
3767  unsigned tmp;
3768  if (b > a)
3769    {
3770      tmp = a;
3771      a = b;
3772      b = tmp;
3773    }
3774  while (1)
3775    {
3776      tmp = a % b;
3777      if (tmp == 0)
3778	return b;
3779      a = b;
3780      b = tmp;
3781    }
3782}
3783
3784static unsigned
3785lcm(unsigned a, unsigned b)
3786{
3787  if (a == b)
3788    return a;
3789  else
3790    return a * b / gcd(a, b);
3791}
3792
3793static int
3794adjusted_vertical_resolution(const res_t *res)
3795{
3796  if (res->vres >= 720)
3797    return res->vres;
3798  else if (res->hres >= 720)	/* Special case 720x360 */
3799    return 720;
3800  else if (res->vres % 90 == 0)
3801    return res->vres;
3802  else
3803    return lcm(res->hres, res->vres);
3804}
3805
3806static int
3807adjusted_horizontal_resolution(const res_t *res)
3808{
3809  if (res->vres % 90 == 0)
3810    return res->hres;
3811  else
3812    return lcm(res->hres, res->vres);
3813}
3814
3815static void
3816setup_resolution(stp_vars_t *v)
3817{
3818  escp2_privdata_t *pd = get_privdata(v);
3819  const res_t *res = stp_escp2_find_resolution(v);
3820
3821  int vertical = adjusted_vertical_resolution(res);
3822  int horizontal = adjusted_horizontal_resolution(res);
3823
3824  pd->res = res;
3825  pd->use_extended_commands =
3826    escp2_use_extended_commands(v, !(pd->res->command));
3827  pd->physical_xdpi = escp2_base_res(v);
3828  if (pd->physical_xdpi > pd->res->hres)
3829    pd->physical_xdpi = pd->res->hres;
3830
3831  if (pd->use_extended_commands)
3832    {
3833      pd->unit_scale = MAX(escp2_max_hres(v), escp2_max_vres(v));
3834      pd->horizontal_units = horizontal;
3835      pd->micro_units = horizontal;
3836    }
3837  else
3838    {
3839      pd->unit_scale = 3600;
3840      if (pd->res->hres <= 720)
3841	pd->micro_units = vertical;
3842      else
3843	pd->micro_units = horizontal;
3844      pd->horizontal_units = vertical;
3845    }
3846  /* Note hard-coded 1440 -- from Epson manuals */
3847  if (stp_escp2_has_cap(v, MODEL_COMMAND, MODEL_COMMAND_1999) &&
3848      stp_escp2_has_cap(v, MODEL_VARIABLE_DOT, MODEL_VARIABLE_NO))
3849    pd->micro_units = 1440;
3850  pd->vertical_units = vertical;
3851  pd->page_management_units = vertical;
3852}
3853
3854static void
3855setup_softweave_parameters(stp_vars_t *v)
3856{
3857  escp2_privdata_t *pd = get_privdata(v);
3858  pd->horizontal_passes = pd->res->printed_hres / pd->physical_xdpi;
3859  if (pd->physical_channels == 1 &&
3860      (pd->inkname->channels[0].subchannels->split_channel_count > 1 ||
3861       (pd->res->vres >=
3862	(escp2_base_separation(v) / escp2_black_nozzle_separation(v)))) &&
3863      (escp2_max_black_resolution(v) < 0 ||
3864       pd->res->vres <= escp2_max_black_resolution(v)) &&
3865      escp2_black_nozzles(v))
3866    pd->use_black_parameters = 1;
3867  else
3868    pd->use_black_parameters = 0;
3869  if (pd->use_fast_360)
3870    {
3871      pd->nozzles = escp2_fast_nozzles(v);
3872      pd->nozzle_separation = escp2_fast_nozzle_separation(v);
3873      pd->nozzle_start = escp2_fast_nozzle_start(v);
3874      pd->min_nozzles = escp2_min_fast_nozzles(v);
3875    }
3876  else if (pd->use_black_parameters)
3877    {
3878      pd->nozzles = escp2_black_nozzles(v);
3879      pd->nozzle_separation = escp2_black_nozzle_separation(v);
3880      pd->nozzle_start = escp2_black_nozzle_start(v);
3881      pd->min_nozzles = escp2_min_black_nozzles(v);
3882    }
3883  else
3884    {
3885      pd->nozzles = escp2_nozzles(v);
3886      pd->nozzle_separation = escp2_nozzle_separation(v);
3887      pd->nozzle_start = escp2_nozzle_start(v);
3888      pd->min_nozzles = escp2_min_nozzles(v);
3889    }
3890}
3891
3892static void
3893setup_printer_weave_parameters(stp_vars_t *v)
3894{
3895  escp2_privdata_t *pd = get_privdata(v);
3896  pd->horizontal_passes = 1;
3897  pd->nozzles = 1;
3898  pd->nozzle_separation = 1;
3899  pd->nozzle_start = 0;
3900  pd->min_nozzles = 1;
3901  if (pd->physical_channels == 1)
3902    pd->use_black_parameters = 1;
3903  else
3904    pd->use_black_parameters = 0;
3905  pd->extra_vertical_passes = 1;
3906}
3907
3908static void
3909setup_head_parameters(stp_vars_t *v)
3910{
3911  escp2_privdata_t *pd = get_privdata(v);
3912  /*
3913   * Set up the output channels
3914   */
3915  if (strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") == 0)
3916    pd->logical_channels = 1;
3917  else
3918    pd->logical_channels = pd->inkname->channel_count;
3919
3920  pd->physical_channels =
3921    compute_channel_count(pd->inkname, pd->logical_channels,
3922			  pd->use_aux_channels);
3923  if (pd->physical_channels == 0)
3924    {
3925      pd->inkname = stpi_escp2_get_default_black_inkset();
3926      pd->physical_channels =
3927	compute_channel_count(pd->inkname, pd->logical_channels,
3928			      pd->use_aux_channels);
3929    }
3930
3931  pd->printer_weave = get_printer_weave(v);
3932
3933  pd->extra_vertical_passes = 1;
3934  if (stp_check_int_parameter(v, "BandEnhancement", STP_PARAMETER_ACTIVE))
3935    pd->extra_vertical_passes =
3936      1 << stp_get_int_parameter(v, "BandEnhancement");
3937  if (stp_escp2_has_cap(v, MODEL_FAST_360, MODEL_FAST_360_YES) &&
3938      (pd->inkname->inkset == INKSET_CMYK || pd->physical_channels == 1) &&
3939      pd->res->hres == pd->physical_xdpi && pd->res->vres == 360)
3940    pd->use_fast_360 = 1;
3941  else
3942    pd->use_fast_360 = 0;
3943
3944  /*
3945   * Set up the printer-specific parameters (weaving)
3946   */
3947  pd->use_printer_weave = use_printer_weave(v);
3948  if (pd->use_printer_weave)
3949    setup_printer_weave_parameters(v);
3950  else
3951    setup_softweave_parameters(v);
3952  pd->separation_rows = escp2_separation_rows(v);
3953  pd->pseudo_separation_rows = escp2_pseudo_separation_rows(v);
3954  pd->extra_720dpi_separation = escp2_extra_720dpi_separation(v);
3955  pd->bidirectional_upper_limit = escp2_bidirectional_upper_limit(v);
3956
3957  if (pd->horizontal_passes == 0)
3958    pd->horizontal_passes = 1;
3959
3960  setup_head_offset(v);
3961  setup_split_channels(v);
3962
3963  if (stp_check_string_parameter(v, "Duplex", STP_PARAMETER_ACTIVE))
3964    {
3965      const char *duplex = stp_get_string_parameter(v, "Duplex");
3966      if (strcmp(duplex, "DuplexTumble") == 0)
3967	pd->duplex = DUPLEX_TUMBLE;
3968      else if (strcmp(duplex, "DuplexNoTumble") == 0)
3969	pd->duplex = DUPLEX_NO_TUMBLE;
3970      else
3971	pd->duplex = 0;
3972    }
3973  if (pd->duplex)
3974    pd->extra_vertical_feed = 0;
3975  else
3976    pd->extra_vertical_feed = escp2_extra_feed(v);
3977
3978  if (strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") == 0 &&
3979      pd->physical_channels == 1)
3980    {
3981      if (pd->use_black_parameters)
3982	pd->initial_vertical_offset =
3983	  (escp2_black_initial_vertical_offset(v) -
3984	   (escp2_black_nozzle_start(v) * (int) escp2_black_nozzle_separation(v))) *
3985	  pd->page_management_units /
3986	  escp2_base_separation(v);
3987      else
3988	pd->initial_vertical_offset = pd->head_offset[0] +
3989	  ((escp2_initial_vertical_offset(v) -
3990	    (escp2_nozzle_start(v) * (int) escp2_nozzle_separation(v))) *
3991	   pd->page_management_units / escp2_base_separation(v));
3992    }
3993  else
3994    pd->initial_vertical_offset =
3995      (escp2_initial_vertical_offset(v) -
3996       (escp2_nozzle_start(v) * (int) escp2_nozzle_separation(v))) *
3997      pd->page_management_units /
3998      escp2_base_separation(v);
3999
4000  pd->printing_initial_vertical_offset = 0;
4001  pd->bitwidth = escp2_bits(v);
4002}
4003
4004static void
4005setup_page(stp_vars_t *v)
4006{
4007  escp2_privdata_t *pd = get_privdata(v);
4008  const input_slot_t *input_slot = stp_escp2_get_input_slot(v);
4009  int extra_left = 0;
4010  int extra_top = 0;
4011  int hub_size = 0;
4012  int min_horizontal_alignment = escp2_min_horizontal_position_alignment(v);
4013  int base_horizontal_alignment =
4014    pd->res->hres / escp2_base_horizontal_position_alignment(v);
4015  int required_horizontal_alignment =
4016    MAX(min_horizontal_alignment, base_horizontal_alignment);
4017
4018  const char *cd_type = stp_get_string_parameter(v, "PageSize");
4019  if (cd_type && (strcmp(cd_type, "CDCustom") == 0 ))
4020     {
4021	int outer_diameter = stp_get_dimension_parameter(v, "CDOuterDiameter");
4022	stp_set_page_width(v, outer_diameter);
4023	stp_set_page_height(v, outer_diameter);
4024	stp_set_width(v, outer_diameter);
4025	stp_set_height(v, outer_diameter);
4026	hub_size = stp_get_dimension_parameter(v, "CDInnerDiameter");
4027     }
4028 else
4029    {
4030	const char *inner_radius_name = stp_get_string_parameter(v, "CDInnerRadius");
4031  	hub_size = 43 * 10 * 72 / 254;	/* 43 mm standard CD hub */
4032
4033  	if (inner_radius_name && strcmp(inner_radius_name, "Small") == 0)
4034   	  hub_size = 16 * 10 * 72 / 254;	/* 15 mm prints to the hole - play it
4035				   safe and print 16 mm */
4036    }
4037
4038  escp2_media_size(v, &(pd->page_true_width), &(pd->page_true_height));
4039  /* Don't use full bleed mode if the paper itself has a margin */
4040  if (pd->page_left > 0 || pd->page_top > 0)
4041    stp_set_boolean_parameter(v, "FullBleed", 0);
4042  if (stp_escp2_has_cap(v, MODEL_ZEROMARGIN, MODEL_ZEROMARGIN_FULL) &&
4043      ((!input_slot || !(input_slot->is_cd))))
4044    {
4045      pd->page_extra_height =
4046	max_nozzle_span(v) * pd->page_management_units /
4047	escp2_base_separation(v);
4048      if (stp_get_boolean_parameter(v, "FullBleed"))
4049	pd->paper_extra_bottom = 0;
4050      else
4051	pd->paper_extra_bottom = escp2_paper_extra_bottom(v);
4052    }
4053  else if (stp_escp2_has_cap(v, MODEL_ZEROMARGIN, MODEL_ZEROMARGIN_YES) &&
4054	   (stp_get_boolean_parameter(v, "FullBleed")) &&
4055	   ((!input_slot || !(input_slot->is_cd))))
4056    {
4057      pd->paper_extra_bottom = 0;
4058      pd->page_extra_height =
4059	escp2_zero_margin_offset(v) * pd->page_management_units /
4060	escp2_base_separation(v);
4061    }
4062  else if (stp_escp2_has_cap(v, MODEL_ZEROMARGIN, MODEL_ZEROMARGIN_RESTR) &&
4063	   (stp_get_boolean_parameter(v, "FullBleed")) &&
4064	   ((!input_slot || !(input_slot->is_cd))))
4065    {
4066      pd->paper_extra_bottom = 0;
4067      pd->page_extra_height = 0;
4068    }
4069  else if (stp_escp2_printer_supports_duplex(v) && !pd->duplex)
4070    {
4071      pd->paper_extra_bottom = escp2_paper_extra_bottom(v);
4072      pd->page_extra_height =
4073	max_nozzle_span(v) * pd->page_management_units /
4074	escp2_base_separation(v);
4075    }
4076  else
4077    {
4078      if (input_slot)
4079	pd->page_extra_height = input_slot->extra_height *
4080	  pd->page_management_units / escp2_base_separation(v);
4081      else
4082	pd->page_extra_height = 0;
4083      pd->paper_extra_bottom = escp2_paper_extra_bottom(v);
4084    }
4085  internal_imageable_area(v, 0, 0, &pd->page_left, &pd->page_right,
4086			  &pd->page_bottom, &pd->page_top);
4087
4088  if (input_slot && input_slot->is_cd && escp2_cd_x_offset(v) > 0)
4089    {
4090      int left_center = escp2_cd_x_offset(v) +
4091	stp_get_dimension_parameter(v, "CDXAdjustment");
4092      int top_center = escp2_cd_y_offset(v) +
4093	stp_get_dimension_parameter(v, "CDYAdjustment");
4094      pd->page_true_height = pd->page_bottom - pd->page_top;
4095      pd->page_true_width = pd->page_right - pd->page_left;
4096      pd->paper_extra_bottom = 0;
4097      pd->page_extra_height = 0;
4098      stp_set_left(v, stp_get_left(v) - pd->page_left);
4099      stp_set_top(v, stp_get_top(v) - pd->page_top);
4100      pd->page_right -= pd->page_left;
4101      pd->page_bottom -= pd->page_top;
4102      pd->page_top = 0;
4103      pd->page_left = 0;
4104      extra_top = top_center - (pd->page_bottom / 2);
4105      extra_left = left_center - (pd->page_right / 2);
4106      pd->cd_inner_radius = hub_size * pd->micro_units / 72 / 2;
4107      pd->cd_outer_radius = pd->page_right * pd->micro_units / 72 / 2;
4108      pd->cd_x_offset =
4109	((pd->page_right / 2) - stp_get_left(v)) * pd->micro_units / 72;
4110      pd->cd_y_offset = stp_get_top(v) * pd->res->printed_vres / 72;
4111      if (escp2_cd_page_height(v))
4112	{
4113	  pd->page_right = escp2_cd_page_width(v);
4114	  pd->page_bottom = escp2_cd_page_height(v);
4115	  pd->page_true_height = escp2_cd_page_height(v);
4116	  pd->page_true_width = escp2_cd_page_width(v);
4117	}
4118    }
4119
4120  pd->page_right += extra_left + 1;
4121  pd->page_width = pd->page_right - pd->page_left;
4122  pd->image_left = stp_get_left(v) - pd->page_left + extra_left;
4123  pd->image_width = stp_get_width(v);
4124  pd->image_scaled_width = pd->image_width * pd->res->hres / 72;
4125  pd->image_printed_width = pd->image_width * pd->res->printed_hres / 72;
4126  if (pd->split_channel_count >= 1)
4127    {
4128      pd->split_channel_width =
4129	((pd->image_printed_width + pd->horizontal_passes - 1) /
4130	 pd->horizontal_passes);
4131      pd->split_channel_width = (pd->split_channel_width + 7) / 8;
4132      pd->split_channel_width *= pd->bitwidth;
4133      if (!(stp_get_debug_level() & STP_DBG_NO_COMPRESSION))
4134	{
4135	  pd->comp_buf =
4136	    stp_malloc(stp_compute_tiff_linewidth(v, pd->split_channel_width));
4137	}
4138    }
4139  pd->image_left_position = pd->image_left * pd->micro_units / 72;
4140  pd->zero_margin_offset = escp2_zero_margin_offset(v);
4141  if (supports_borderless(v) &&
4142      pd->advanced_command_set &&
4143      ((!input_slot || !(input_slot->is_cd)) &&
4144       stp_get_boolean_parameter(v, "FullBleed")))
4145    {
4146      int margin = escp2_micro_left_margin(v);
4147      int sep = escp2_base_separation(v);
4148      pd->image_left_position +=
4149	(pd->zero_margin_offset - margin) * pd->micro_units / sep;
4150    }
4151  /*
4152   * Many printers print extremely slowly if the starting position
4153   * is not aligned to 1/180"
4154   */
4155  if (required_horizontal_alignment > 1)
4156    pd->image_left_position =
4157      (pd->image_left_position / required_horizontal_alignment) *
4158      required_horizontal_alignment;
4159
4160
4161  pd->page_bottom += extra_top + 1;
4162  pd->page_height = pd->page_bottom - pd->page_top;
4163  pd->image_top = stp_get_top(v) - pd->page_top + extra_top;
4164  pd->image_height = stp_get_height(v);
4165  pd->image_scaled_height = pd->image_height * pd->res->vres / 72;
4166  pd->image_printed_height = pd->image_height * pd->res->printed_vres / 72;
4167
4168  if (input_slot && input_slot->roll_feed_cut_flags)
4169    {
4170      pd->page_true_height += 4; /* Empirically-determined constants */
4171      pd->page_top += 2;
4172      pd->page_bottom += 2;
4173      pd->image_top += 2;
4174      pd->page_height += 2;
4175    }
4176}
4177
4178static void
4179set_mask(unsigned char *cd_mask, int x_center, int scaled_x_where,
4180	 int limit, int expansion, int invert)
4181{
4182  int clear_val = invert ? 255 : 0;
4183  int set_val = invert ? 0 : 255;
4184  int bytesize = 8 / expansion;
4185  int byteextra = bytesize - 1;
4186  int first_x_on = x_center - scaled_x_where;
4187  int first_x_off = x_center + scaled_x_where;
4188  if (first_x_on < 0)
4189    first_x_on = 0;
4190  if (first_x_on > limit)
4191    first_x_on = limit;
4192  if (first_x_off < 0)
4193    first_x_off = 0;
4194  if (first_x_off > limit)
4195    first_x_off = limit;
4196  first_x_on += byteextra;
4197  if (first_x_off > (first_x_on - byteextra))
4198    {
4199      int first_x_on_byte = first_x_on / bytesize;
4200      int first_x_on_mod = expansion * (byteextra - (first_x_on % bytesize));
4201      int first_x_on_extra = ((1 << first_x_on_mod) - 1) ^ clear_val;
4202      int first_x_off_byte = first_x_off / bytesize;
4203      int first_x_off_mod = expansion * (byteextra - (first_x_off % bytesize));
4204      int first_x_off_extra = ((1 << 8) - (1 << first_x_off_mod)) ^ clear_val;
4205      if (first_x_off_byte < first_x_on_byte)
4206	{
4207	  /* This can happen, if 6 or fewer points are turned on */
4208	  cd_mask[first_x_on_byte] = first_x_on_extra & first_x_off_extra;
4209	}
4210      else
4211	{
4212	  if (first_x_on_extra != clear_val)
4213	    cd_mask[first_x_on_byte - 1] = first_x_on_extra;
4214	  if (first_x_off_byte > first_x_on_byte)
4215	    memset(cd_mask + first_x_on_byte, set_val,
4216		   first_x_off_byte - first_x_on_byte);
4217	  if (first_x_off_extra != clear_val)
4218	    cd_mask[first_x_off_byte] = first_x_off_extra;
4219	}
4220    }
4221}
4222
4223static int
4224escp2_print_data(stp_vars_t *v, stp_image_t *image)
4225{
4226  escp2_privdata_t *pd = get_privdata(v);
4227  int errdiv  = stp_image_height(image) / pd->image_printed_height;
4228  int errmod  = stp_image_height(image) % pd->image_printed_height;
4229  int errval  = 0;
4230  int errlast = -1;
4231  int errline  = 0;
4232  int y;
4233  double outer_r_sq = 0;
4234  double inner_r_sq = 0;
4235  int x_center = pd->cd_x_offset * pd->res->printed_hres / pd->micro_units;
4236  unsigned char *cd_mask = NULL;
4237  if (pd->cd_outer_radius > 0)
4238    {
4239      cd_mask = stp_malloc(1 + (pd->image_printed_width + 7) / 8);
4240      outer_r_sq = (double) pd->cd_outer_radius * (double) pd->cd_outer_radius;
4241      inner_r_sq = (double) pd->cd_inner_radius * (double) pd->cd_inner_radius;
4242    }
4243
4244  for (y = 0; y < pd->image_printed_height; y ++)
4245    {
4246      int duplicate_line = 1;
4247      unsigned zero_mask;
4248
4249      if (errline != errlast)
4250	{
4251	  errlast = errline;
4252	  duplicate_line = 0;
4253	  if (stp_color_get_row(v, image, errline, &zero_mask))
4254	    return 2;
4255	}
4256
4257      if (cd_mask)
4258	{
4259	  int y_distance_from_center =
4260	    pd->cd_outer_radius -
4261	    ((y + pd->cd_y_offset) * pd->micro_units / pd->res->printed_vres);
4262	  if (y_distance_from_center < 0)
4263	    y_distance_from_center = -y_distance_from_center;
4264	  memset(cd_mask, 0, (pd->image_printed_width + 7) / 8);
4265	  if (y_distance_from_center < pd->cd_outer_radius)
4266	    {
4267	      double y_sq = (double) y_distance_from_center *
4268		(double) y_distance_from_center;
4269	      int x_where = sqrt(outer_r_sq - y_sq) + .5;
4270	      int scaled_x_where = x_where * pd->res->printed_hres / pd->micro_units;
4271	      set_mask(cd_mask, x_center, scaled_x_where,
4272		       pd->image_printed_width, 1, 0);
4273	      if (y_distance_from_center < pd->cd_inner_radius)
4274		{
4275		  x_where = sqrt(inner_r_sq - y_sq) + .5;
4276		  scaled_x_where = x_where * pd->res->printed_hres / pd->micro_units;
4277		  set_mask(cd_mask, x_center, scaled_x_where,
4278			   pd->image_printed_width, 1, 1);
4279		}
4280	    }
4281	}
4282
4283      stp_dither(v, y, duplicate_line, zero_mask, cd_mask);
4284
4285      stp_write_weave(v, pd->cols);
4286      errval += errmod;
4287      errline += errdiv;
4288      if (errval >= pd->image_printed_height)
4289	{
4290	  errval -= pd->image_printed_height;
4291	  errline ++;
4292	}
4293    }
4294  if (cd_mask)
4295    stp_free(cd_mask);
4296  return 1;
4297}
4298
4299static int
4300escp2_print_page(stp_vars_t *v, stp_image_t *image)
4301{
4302  int status;
4303  escp2_privdata_t *pd = get_privdata(v);
4304  int out_channels;		/* Output bytes per pixel */
4305  int line_width = (pd->image_printed_width + 7) / 8 * pd->bitwidth;
4306  int weave_pattern = STP_WEAVE_ZIGZAG;
4307  if (stp_check_string_parameter(v, "Weave", STP_PARAMETER_ACTIVE))
4308    {
4309      const char *weave = stp_get_string_parameter(v, "Weave");
4310      if (strcmp(weave, "Alternate") == 0)
4311	weave_pattern = STP_WEAVE_ZIGZAG;
4312      else if (strcmp(weave, "Ascending") == 0)
4313	weave_pattern = STP_WEAVE_ASCENDING;
4314      else if (strcmp(weave, "Descending") == 0)
4315	weave_pattern = STP_WEAVE_DESCENDING;
4316      else if (strcmp(weave, "Ascending2X") == 0)
4317	weave_pattern = STP_WEAVE_ASCENDING_2X;
4318      else if (strcmp(weave, "Staggered") == 0)
4319	weave_pattern = STP_WEAVE_STAGGERED;
4320    }
4321
4322  stp_initialize_weave
4323    (v,
4324     pd->nozzles,
4325     pd->nozzle_separation * pd->res->vres / escp2_base_separation(v),
4326     pd->horizontal_passes,
4327     pd->res->vertical_passes * pd->extra_vertical_passes,
4328     1,
4329     pd->channels_in_use,
4330     pd->bitwidth,
4331     pd->image_printed_width,
4332     pd->image_printed_height,
4333     ((pd->page_extra_height * pd->res->vres / pd->vertical_units) +
4334      (pd->image_top * pd->res->vres / 72)),
4335     (pd->page_extra_height +
4336      (pd->page_height + pd->extra_vertical_feed) * pd->res->vres / 72),
4337     pd->head_offset,
4338     weave_pattern,
4339     stpi_escp2_flush_pass,
4340     (((stp_get_debug_level() & STP_DBG_NO_COMPRESSION) ||
4341       pd->split_channel_count > 0) ?
4342      stp_fill_uncompressed : stp_fill_tiff),
4343     (((stp_get_debug_level() & STP_DBG_NO_COMPRESSION) ||
4344       pd->split_channel_count > 0) ?
4345      stp_pack_uncompressed : stp_pack_tiff),
4346     (((stp_get_debug_level() & STP_DBG_NO_COMPRESSION) ||
4347       pd->split_channel_count > 0) ?
4348      stp_compute_uncompressed_linewidth : stp_compute_tiff_linewidth));
4349
4350  stp_dither_init(v, image, pd->image_printed_width, pd->res->printed_hres,
4351		  pd->res->printed_vres);
4352  allocate_channels(v, line_width);
4353  adjust_print_quality(v);
4354  out_channels = stp_color_init(v, image, 65536);
4355
4356/*  stpi_dither_set_expansion(v, pd->res->hres / pd->res->printed_hres); */
4357
4358  setup_inks(v);
4359
4360  status = escp2_print_data(v, image);
4361  stp_flush_all(v);
4362  stpi_escp2_terminate_page(v);
4363  return status;
4364}
4365
4366/*
4367 * 'escp2_print()' - Print an image to an EPSON printer.
4368 */
4369static int
4370escp2_do_print(stp_vars_t *v, stp_image_t *image, int print_op)
4371{
4372  int status = 1;
4373  int i;
4374
4375  escp2_privdata_t *pd;
4376  int page_number = stp_get_int_parameter(v, "PageNumber");
4377
4378  if (!stp_verify(v))
4379    {
4380      stp_eprintf(v, _("Print options not verified; cannot print.\n"));
4381      return 0;
4382    }
4383
4384  if (strcmp(stp_get_string_parameter(v, "InputImageType"), "Raw") == 0 &&
4385      !set_raw_ink_type(v))
4386    return 0;
4387
4388  pd = (escp2_privdata_t *) stp_zalloc(sizeof(escp2_privdata_t));
4389
4390  pd->printed_something = 0;
4391  pd->last_color = -1;
4392  pd->last_pass_offset = 0;
4393  pd->last_pass = -1;
4394  pd->send_zero_pass_advance =
4395    stp_escp2_has_cap(v, MODEL_SEND_ZERO_ADVANCE, MODEL_SEND_ZERO_ADVANCE_YES);
4396  stp_allocate_component_data(v, "Driver", NULL, NULL, pd);
4397
4398  pd->inkname = get_inktype(v);
4399  if (pd->inkname->inkset != INKSET_EXTENDED &&
4400      stp_check_boolean_parameter(v, "UseGloss", STP_PARAMETER_ACTIVE) &&
4401      stp_get_boolean_parameter(v, "UseGloss"))
4402    pd->use_aux_channels = 1;
4403  else
4404    pd->use_aux_channels = 0;
4405  if (pd->inkname && pd->inkname->inkset == INKSET_QUADTONE &&
4406      strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
4407    {
4408      stp_eprintf(v, "Warning: Quadtone inkset only available in MONO\n");
4409      stp_set_string_parameter(v, "PrintingMode", "BW");
4410    }
4411  if (pd->inkname && pd->inkname->inkset == INKSET_HEXTONE &&
4412      strcmp(stp_get_string_parameter(v, "PrintingMode"), "BW") != 0)
4413    {
4414      stp_eprintf(v, "Warning: Hextone inkset only available in MONO\n");
4415      stp_set_string_parameter(v, "PrintingMode", "BW");
4416    }
4417  pd->channels_in_use = count_channels(pd->inkname, pd->use_aux_channels);
4418
4419  setup_basic(v);
4420  setup_resolution(v);
4421  setup_head_parameters(v);
4422  setup_page(v);
4423  setup_misc(v);
4424
4425  adjust_density_and_ink_type(v);
4426  if (print_op & OP_JOB_START)
4427    stpi_escp2_init_printer(v);
4428  if (print_op & OP_JOB_PRINT)
4429    {
4430      stp_image_init(image);
4431      if ((page_number & 1) && pd->duplex &&
4432	  ((pd->duplex & pd->input_slot->duplex) == 0))
4433	  /* If the hardware can't do the duplex operation, we need to
4434	     emulate it in software */
4435	image = stpi_buffer_image(image, BUFFER_FLAG_FLIP_X | BUFFER_FLAG_FLIP_Y);
4436      status = escp2_print_page(v, image);
4437      stp_image_conclude(image);
4438    }
4439  if (print_op & OP_JOB_END)
4440    stpi_escp2_deinit_printer(v);
4441
4442  if (pd->head_offset)
4443    stp_free(pd->head_offset);
4444
4445  /*
4446   * Cleanup...
4447   */
4448  if (pd->cols)
4449    {
4450      for (i = 0; i < pd->channels_in_use; i++)
4451	if (pd->cols[i])
4452	  stp_free(pd->cols[i]);
4453      stp_free(pd->cols);
4454    }
4455  if (pd->media_settings)
4456    stp_vars_destroy(pd->media_settings);
4457  if (pd->channels)
4458    stp_free(pd->channels);
4459  if (pd->split_channels)
4460    stp_free(pd->split_channels);
4461  if (pd->comp_buf)
4462    stp_free(pd->comp_buf);
4463  stp_free(pd);
4464
4465  return status;
4466}
4467
4468static int
4469escp2_print(const stp_vars_t *v, stp_image_t *image)
4470{
4471  stp_vars_t *nv = stp_vars_create_copy(v);
4472  int op = OP_JOB_PRINT;
4473  int status;
4474  if (!stp_get_string_parameter(v, "JobMode") ||
4475      strcmp(stp_get_string_parameter(v, "JobMode"), "Page") == 0)
4476    op = OP_JOB_START | OP_JOB_PRINT | OP_JOB_END;
4477  stp_prune_inactive_options(nv);
4478  status = escp2_do_print(nv, image, op);
4479  stp_vars_destroy(nv);
4480  return status;
4481}
4482
4483static int
4484escp2_job_start(const stp_vars_t *v, stp_image_t *image)
4485{
4486  stp_vars_t *nv = stp_vars_create_copy(v);
4487  int status;
4488  stp_prune_inactive_options(nv);
4489  status = escp2_do_print(nv, image, OP_JOB_START);
4490  stp_vars_destroy(nv);
4491  return status;
4492}
4493
4494static int
4495escp2_job_end(const stp_vars_t *v, stp_image_t *image)
4496{
4497  stp_vars_t *nv = stp_vars_create_copy(v);
4498  int status;
4499  stp_prune_inactive_options(nv);
4500  status = escp2_do_print(nv, image, OP_JOB_END);
4501  stp_vars_destroy(nv);
4502  return status;
4503}
4504
4505static const stp_printfuncs_t print_escp2_printfuncs =
4506{
4507  escp2_list_parameters,
4508  escp2_parameters,
4509  escp2_media_size,
4510  escp2_imageable_area,
4511  escp2_maximum_imageable_area,
4512  escp2_limit,
4513  escp2_print,
4514  escp2_describe_resolution,
4515  escp2_describe_output,
4516  stp_verify_printer_params,
4517  escp2_job_start,
4518  escp2_job_end,
4519  NULL
4520};
4521
4522static stp_family_t print_escp2_module_data =
4523  {
4524    &print_escp2_printfuncs,
4525    NULL
4526  };
4527
4528
4529static int
4530print_escp2_module_init(void)
4531{
4532  hue_curve_bounds = stp_curve_create_from_string
4533    ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
4534     "<gutenprint>\n"
4535     "<curve wrap=\"wrap\" type=\"linear\" gamma=\"0\">\n"
4536     "<sequence count=\"2\" lower-bound=\"0\" upper-bound=\"1\">\n"
4537     "1 1\n"
4538     "</sequence>\n"
4539     "</curve>\n"
4540     "</gutenprint>");
4541  return stp_family_register(print_escp2_module_data.printer_list);
4542}
4543
4544
4545static int
4546print_escp2_module_exit(void)
4547{
4548  return stp_family_unregister(print_escp2_module_data.printer_list);
4549}
4550
4551
4552/* Module header */
4553#define stp_module_version print_escp2_LTX_stp_module_version
4554#define stp_module_data print_escp2_LTX_stp_module_data
4555
4556stp_module_version_t stp_module_version = {0, 0};
4557
4558stp_module_t stp_module_data =
4559  {
4560    "escp2",
4561    VERSION,
4562    "Epson family driver",
4563    STP_MODULE_CLASS_FAMILY,
4564    NULL,
4565    print_escp2_module_init,
4566    print_escp2_module_exit,
4567    (void *) &print_escp2_module_data
4568  };
4569
4570