#!@PERL@ # Get command line options use Getopt::Long; Getopt::Long::Configure("no_ignore_case", "pass_through"); GetOptions("m=s" => \$opt_m, # Map file "t=s" => \$opt_t, # Template directory "f=s" => \$opt_f, # Foomatic version "d=s" => \@opt_d); # which Drivers? # Wherever we put it... my $mapfile; # $mapfile = "../main/printers.xml"; $mapfile = $opt_m; my $templatedir; $templatedir = $opt_t; my @drivertypes = (); for my $d (@opt_d) { if (lc($d) eq "gs") { push (@drivertypes, "gs"); } if (lc($d) eq "ijs") { push (@drivertypes, "ijs"); } if (lc($d) eq "nogs") { @drivertypes = ("ijs"); } if (lc($d) eq "noijs") { @drivertypes = ("gs"); } if (lc($d) eq "all") { @drivertypes = ("gs", "ijs"); } if (lc($d) eq "both") { @drivertypes = ("gs", "ijs"); } } exit 0 if ($#drivertypes < 0); # Nothing to be done, exit silently # Do we have Foomatic 2.9 or newer my $foomatic3 = ((defined($opt_f)) && ($opt_f >= 2.9)); # The following substitutions happen to the template XML files: # @@STPVER@@ - the version number (ie '4.1.5') # @@DRVNAME@@ - driver name (ie gimp-print) # @@STPRINTERS@@ - ... clause for the driver # @@OPTCONSTRAINTS@@ - ... object for the option # @@ENUMVALS@@ - ... section for the enum option # @@MINVAL@@ - minimum value for numeric setting # @@MAXVAL@@ - maximum value for numeric setting # @@DEFVAL@@ - default value for numeric setting # For some things, there are option-specific root-around-n-writer # functions. So we have a dispatch table: # # function arguments are: ('StpDriverOptionName') my $funcs = { 'InputSlot' => { 'OPTCONSTRAINTS' => \&build_cons, 'ENUMVALS' => \&build_ev, }, 'InkType' => { 'OPTCONSTRAINTS' => \&build_cons, 'ENUMVALS' => \&build_ev }, 'MediaType' => { 'OPTCONSTRAINTS' => \&build_cons, 'ENUMVALS' => \&build_ev }, 'Model' => { 'OPTCONSTRAINTS' => \&build_model_cons, 'ENUMVALS' => \&build_model_ev }, 'PageSize' => { 'OPTCONSTRAINTS' => \&build_cons, 'ENUMVALS' => \&build_ev }, 'Quality' => { 'OPTCONSTRAINTS' => \&build_cons, 'ENUMVALS' => \&build_ev }, 'Dither' => { 'OPTCONSTRAINTS' => \&build_cons, 'ENUMVALS' => \&build_ev }, 'Color' => { 'OPTCONSTRAINTS' => \&build_cons, 'ENUMVALS' => \&build_ev }, 'Resolution' => { 'OPTCONSTRAINTS' => \&build_resolution_cons, 'ENUMVALS' => \&build_resolution_ev }, 'PrintoutMode' => { 'OPTCONSTRAINTS' => \&build_printoutmode_cons, 'ENUMVALS' => \&build_printoutmode_ev }, }; my $drivervals = { 'PageSize' => \&optmap_pagesize, 'Color' => \&optmap_color }; my $debug = 0; # Foomatic name => Gimp-print name %argnamemap = ('Quality' => 'Resolution'); $colormap = { 'gs' => { 'Grayscale' => '0', 'Color' => '1', 'BlackAndWhite' => '2' }, 'ijs' => { 'Grayscale' => 'DeviceGray', 'Color' => 'DeviceRGB', 'BlackAndWhite' => 'DeviceGray -dBitsPerSample=1', 'RawCMYK' => 'DeviceCMYK' } }; use Data::Dumper; open PIPE, "./printer_options|" or die "Cannot run printer_options: $!\n"; print STDERR "Loading options from ./printer_options..."; while() { next if m!^#!; eval $_; } close PIPE or die "Cannot run printer_options: $!\n"; print STDERR "done.\n"; if ($foomatic3) { open PIPE, "./printer_margins|" or die "Cannot run printer_margins: $!\n"; print STDERR "Loading margins from ./printer_margins..."; $code = join('', ); close PIPE or die "Cannot run printer_margins: $!\n"; eval $code or die "Cannot run printer_margins: $!\n"; print STDERR "done.\n"; } open PIPE, "./stp_limits|" or die "Cannot run stp_limits: $!\n"; print STDERR "Loading options from ./stp_limits..."; while() { next if m!^#!; eval $_; } close PIPE or die "Cannot run stp_limits: $!\n"; print STDERR "done.\n"; open PIPE, "./printers|" or die "Cannot run printers: $!\n"; print STDERR "Loading options from ./printers..."; while() { next if m!^#!; eval $_; } close PIPE or die "Cannot run printers: $!\n"; print STDERR "done.\n"; # OK, now %stpdata is a big honking thing, and %defaults is in there, too. # Invert, to build %bar{$optionname} = [ choice1, choice2 ]; my ($a, $otmp, $vtmp); for $a (keys(%stpdata)) { for $otmp (keys %{$stpdata{$a}}) { for $vtmp (keys (%{$stpdata{$a}{$otmp}})) { if (!$seen_evchoice{$otmp}{$vtmp}++) { push (@{$ev_choices{$otmp}}, [ $vtmp, $stpdata{$a}{$otmp}{$vtmp}]); } } } } if ($foomatic3) { # Generate data for "PrintoutMode" option, only needed for # Foomatic 2.9.x or newer print STDERR "Generating data for \"PrintoutMode\" option..."; ($printoutmode, $printoutmodechoices) = getprintoutmode(); print STDERR "done.\n"; # Foomatic >= 2.9: Make a list of all choice entries needed in the # "PrintoutMode" option XML file. Note: every choice ("Draft", # "Normal", ...) will appear several times, but with different # strings in "". Constraints will make only the # right choices getting into the PPD file. Assign a unique ID to # each entry. for $a (keys(%{$printoutmode})) { for $vtmp (keys %{$printoutmode->{$a}}) { my $mode = $printoutmode->{$a}{$vtmp}; if (!$seen_modes{$vtmp}{$mode}++) { if (!defined($nums{$vtmp})) { $nums{$vtmp} = 0; } $nums{$vtmp} ++; $modes{$vtmp}{$mode} = "$vtmp-$nums{$vtmp}"; } } } } # Step 1: construct a map from Model= values to Foomatic model id's # this map is %mapstp. The inverse from model to Model is %mapdb # # Foomatic is supposed to be a superset, so open PRINTERS, $mapfile or die "Cannot open mapfile $mapfile: $!\n"; for () { if (m!^#\s*gptofoo\s+([^\s]+)\s+([^\s]+)!) { push (@{$mapstp{$1}}, $2); $mapfoo{$2} = $1; # do we need? } } $missing_drivers = 0; # Are we missing any stp printers? for (keys(%stpdata)) { if (!defined($mapstp{$_})) { $missing_drivers = 1; warn "No foomatic printer IDs for gimp-print printer $_.\n"; } } for (keys(%mapstp)) { if (!defined($stpdata{$_})) { $missing_drivers = 1; warn "No gimp-print printer for foomatic ID $_.\n"; } } if ($missing_drivers) { die "Cannot continue\n"; } # Figure out version etc open PIPE, "./gimp-print-version|" or die "Cannot run gimp-print-version: $!\n"; my $stpvers = ; close PIPE or die "Cannot run gimp-print-version: $!\n"; chomp $stpvers; # Build clause... my @printerlist = (); push (@printerlist, " \n"); my $p1; for $p1 (keys(%mapstp)) { push (@printerlist, " \n"); for my $id (@{$mapstp{$p1}}) { if ($foomatic3) { # Add unprintable margins (only Foonmatic 2.9.x) push(@printerlist, " \n"); push(@printerlist, " $id\n"); push(@printerlist, " \n"); my ($cleft, $cright, $ctop, $cbottom) = (undef, undef, undef, undef); if (defined($imageableareas{$p1}{'Custom'})) { $cleft = $imageableareas{$p1}{'Custom'}{'left'}; $cright = $imageableareas{$p1}{'Custom'}{'right'}; $ctop = $imageableareas{$p1}{'Custom'}{'top'}; $cbottom = $imageableareas{$p1}{'Custom'}{'bottom'}; push(@printerlist, " \n"); push(@printerlist, " \n"); push(@printerlist, " $cleft\n"); push(@printerlist, " $cright\n"); push(@printerlist, " $ctop\n"); push(@printerlist, " $cbottom\n"); push(@printerlist, " \n"); } for my $ps (keys %{$imageableareas{$p1}}) { next if $ps eq 'Custom'; # We have done "Custom" already my ($left, $right, $top, $bottom, $width, $height); $left = $imageableareas{$p1}{$ps}{'left'}; $right = $imageableareas{$p1}{$ps}{'right'}; $top = $imageableareas{$p1}{$ps}{'top'}; $bottom = $imageableareas{$p1}{$ps}{'bottom'}; $width = $imageableareas{$p1}{$ps}{'width'}; $height = $imageableareas{$p1}{$ps}{'height'}; # If the section serves for this paper size, # do not define an next if ((defined($cleft)) && ($left == $cleft) && ($right == $width - $cright) && ($top == $height - $ctop) && ($bottom == $cbottom)); push(@printerlist, " \n"); push(@printerlist, " \n"); if ($left != $cleft) { push(@printerlist, " $left\n"); } if ($right != $width - $cright) { push(@printerlist, " $right\n"); } if ($top != $height - $ctop) { push(@printerlist, " $top\n"); } if ($bottom != $cbottom) { push(@printerlist, " $bottom" . "\n"); } push(@printerlist, " \n"); } push(@printerlist, " \n"); push(@printerlist, " \n"); } else { # Printer IDs only push(@printerlist, " $id\n"); } } } push (@printerlist, " \n"); $drivernameprefix = "gimp-print"; print STDERR "Using driver name prefix \"$drivernameprefix\"\n"; my $generalsubs = { 'STPVER' => $stpvers, 'DRVNAME' => $drivernameprefix, 'STPRINTERS' => join('', @printerlist) }; my $optiongroups = { 'PageSize' => 'General', 'InputSlot' => 'General', 'MediaType' => 'General', 'InkType' => 'General', 'PrintoutMode' => 'General', 'Resolution' => 'PrintoutMode', 'Quality' => 'PrintoutMode', 'Color' => 'PrintoutMode', 'ImageType' => 'PrintoutMode', 'Dither' => 'PrintoutMode', 'Gamma' => 'Adjustment', 'Density' => 'Adjustment', 'Brightness' => 'Adjustment', 'Contrast' => 'Adjustment', 'Saturation' => 'Adjustment', 'Cyan' => 'Adjustment', 'Magenta' => 'Adjustment', 'Yellow' => 'Adjustment' }; my @numericsubs = ('MINVAL', 'MAXVAL', 'DEFVAL'); # OK, make the db directory... mkdir "foomatic-db", 0755 or die "Cannot create directory foomatic-db: $!\n" unless -d "foomatic-db"; # Now do stuff, already. Do the substitution into each file... my $tmpl; for $drivertype (@drivertypes) { $drivertypesuffix = "-$drivertype"; $drivertypesuffix =~ s/-gs//; my $drvname = "$drivernameprefix$drivertypesuffix"; $generalsubs->{'DRVNAME'} = $drvname; print "Generating Foomatic data for driver \"$drvname\"...\n"; # OK, make the db heirarchy alongside the templates one... mkdir "foomatic-db/$drvname", 0755 or die "Cannot create directory foomatic-db/$drvname: $!\n" unless -d "foomatic-db/$drvname"; mkdir "foomatic-db/$drvname/opt", 0755 or die "Cannot create directory foomatic-db/$drvname/opt: $!\n" unless -d "foomatic-db/$drvname/opt"; mkdir "foomatic-db/$drvname/driver", 0755 or die "Cannot create directory foomatic-db/$drvname/driver: $!\n" unless -d 'foomatic-db/$drvname/driver'; opendir TDIR, "$templatedir-$drivertype" or die "Cannot open templates directory: $!\n"; while ($tmpl=readdir(TDIR)) { # Only XML files next if ($tmpl !~ m!.+\.xml$!); # The "PrintoutMode" option is only supported by Foomatic 2.9.x or # newer next if ((!$foomatic3) && ($tmpl eq "PrintoutMode.xml")); my $fooopt = $tmpl; $fooopt =~ s!\.xml$!!; my $stpopt = $argnamemap{$fooopt}; $stpopt = $fooopt if ! defined ($stpopt); # print STDERR "Argnamemap '$fooopt' => '$stpopt'\n"; open TMPL, "$templatedir-$drivertype/$tmpl"; my @datafile = ; close TMPL; print STDERR "Processing $tmpl..."; my $template = join('',@datafile); # First, do the generic substitutions. my ($substr); for $substr (keys(%$generalsubs)) { my $substitution = $generalsubs->{$substr}; $template =~ s!\@\@$substr\@\@!$substitution!g; } # Put the options into PPD groups (Foomatic >= 2.9) if ($foomatic3) { my $substitution = "\n " . $optiongroups->{$fooopt} . ""; $template =~ s!\@\@GROUP\@\@!$substitution!g; } else { $template =~ s!\@\@GROUP\@\@!!g; } # Now do the numeric substitutions for $substr (@numericsubs) { my $substitution = $stp_values{$substr}{$stpopt}; $template =~ s!\@\@$substr\@\@!$substitution!g; } # Now do special-purpose substitutions for $substr (keys(%{$funcs->{$fooopt}})) { my $substitution = &{$funcs->{$fooopt}{$substr}}($stpopt); if (defined($substitution)) { $template =~ s!\@\@$substr\@\@!$substitution!g; } } # Any more? grep (m!\@\@([^\@]+)\@\@!g && do { warn " Unknown substitution $1 in $tmpl!\n"; }, split("\n",$template)); # Finally, write out the new file # Options are under opt/ my $dbfilename = lc("foomatic-db/$drvname/opt/$drvname-$tmpl"); # Special case the actual driver file under driver/ $dbfilename = "foomatic-db/$drvname/driver/$drvname.xml" if ($tmpl eq 'gimp-print.xml'); open NEWF, "> $dbfilename" or die "Cannot create $dbfilename: $!"; print STDERR "writing $dbfilename..."; print NEWF $template; print STDERR "done.\n"; close NEWF; } closedir TDIR; # The paper size and resolution maps must be regenerated for the next # driver, because the "driverval"s are different for the different # drivers. So delete the caches. undef $pagemap; undef %rescache; } sub get_ev_shortname { my ($val) = @_; $val =~ s/ //g; $val =~ s/\///g; $val =~ s/\://g; return $val; } sub get_ev_key { my ($val, $drv) = @_; return ("ev/$drv-" . get_ev_shortname($val)); } sub build_ev { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; my @vals = (); # OK, now for each enum_val my $ev; for $ev (@{$ev_choices{$stpopt}}) { # Put in the basic choice info: ev names, etc my $ev_longname = @$ev[1]; my $ev_shortname = @$ev[0]; # The GhostScript driver has no "RawCMYK" output type setting next if (($drivertype eq "gs") && ($stpopt eq "Color") && ($ev_shortname eq "RawCMYK")); my $ev_id = get_ev_key($ev_shortname, $drvname); my $ev_driverval; # Either call a per-option function to get the driverval, or # just use the string choice name. if (defined($drivervals->{$stpopt})) { $ev_driverval = &{$drivervals->{$stpopt}}($ev_shortname); die "Undefined driverval for option $stpopt value $ev_shortname!\n" if (! defined($ev)); } else { $ev_driverval = $ev_shortname; } # Remove "Roll" paper sizes, user has to use "Custom" instead. next if (($stpopt eq "PageSize") && ($ev_driverval eq "")); push (@vals, " \n", " $ev_longname\n", " $ev_shortname\n", " $ev_driverval\n", " \n", " \n", " \n", " $drvname\n", " \n"); # Build constraints for this particular choice my $stpprn; for $stpprn (keys(%stpdata)) { my $fooprn; for $fooprn (@{$mapstp{$stpprn}}) { if ($stpdata{$stpprn}{$stpopt}{$ev_shortname}) { # OK, this choice applies to this printer push (@vals, " \n", " \n", " $drvname\n", " $fooprn\n", " \n"); } } } push (@vals, " \n", " \n"); } return join('', "\n", @vals, " \n"); } sub build_cons { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; my @PNCONS = (); # For each stp printer... my $stpname; for $stpname (keys(%stpdata)) { if (0) { print STDERR " Processing gimp-print printer $stpname...\n"; print STDERR " There are no foomatic printers mapped to $stpname!?\n" if !$mapstp{$stpname}; print STDERR " \%stpdata key is {$stpname}{$stpopt}\n"; } # Add to this printer to argument constraints? if ($stpdata{$stpname}{$stpopt}) { # What's the default value? my $stpdef = $defaults{$stpname}{$stpopt}; # If there's no default, then this option doesn't apply to # this printer. if (defined($stpdef)) { my $foodefval = get_ev_key($stpdef, $drvname); if (0) { print STDERR " Default for $stpname/$stpopt is $stpdef aka $foodefval\n"; } my $fooname; for $fooname (@{$mapstp{$stpname}}) { if (0) { print STDERR " Printer $fooname takes option $stpopt.\n"; } push (@PNCONS, " \n", " $drvname\n", " $fooname\n", " $foodefval\n", " \n"); } } } } return join('', "\n", @PNCONS, " \n"); } sub optmap_pagesize { my ($value) = @_; if (!defined $pagemap) { open PUTIL, "./paper_sizes |" or die "Cannot run paper_sizes: $!\n"; while () { chomp; $_ =~ m!^\s*(.+\S)\s+([0-9]+)\s+([0-9]+)\s*$!; my ($name, $width, $height) = ($1, $2, $3); if (($width > 0 and $height > 0) or ($name eq "Custom")) { if ($drivertype eq "gs") { $pagemap->{$name} = "$width $height"; } else { $pagemap->{$name} = "-dDEVICEWIDTHPOINTS=$width -dDEVICEHEIGHTPOINTS=$height"; } # print STDERR "PageSize '$name' driverval '$width $height'\n"; } } close PUTIL; } return $pagemap->{$value} } sub optmap_color { my ($value) = @_; if (defined $colormap->{$drivertype}{$value}) { return $colormap->{$drivertype}{$value}; } else { die "Cannot map output type '$value'\n"; } } sub build_model_cons { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; # OK, this is funky. For each stp model, we have a choice. That # choice is valid for only the foo printers that correspond. For # any given foo printer, there is *exactly one* available choice. # The defval is the one available choice. Backends and # applications do not show options with only one choice; they just # select that choice. So we don't bother to make pretty option # names or anything. # # See also build_model_ev() my @PNCONS = (); # For each stp printer... my $stpname; for $stpname (keys(%mapstp)) { # For each possible foo name my $fooname; for $fooname (@{$mapstp{$stpname}}) { # What's the default value? my $foodefval = get_ev_key($stpname, $drvname); push (@PNCONS, " \n", " $drvname\n", " $fooname\n", " $foodefval\n", " \n"); } } return join('', "\n", @PNCONS, " \n"); } # See build_model_cons, above. sub build_model_ev { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; my @vals = (); # OK, now for each enum_val my $ev; for $ev (keys(%mapstp)) { # Put in the basic choice info: ev names, etc my $ev_shortname = $ev; my $ev_longname = $printer_name{$ev}; my $ev_shortname = get_ev_shortname($ev); my $ev_id = get_ev_key($ev, $drvname); my $ev_driverval; if ($drivertype eq "gs") { $ev_driverval = $ev; } else { my $make; if ($ev =~ /^escp2/) { $make = "EPSON"; } elsif ($ev =~ /^bjc/) { $make = "CANON"; } elsif ($ev =~ /^pcl/) { $make = "HEWLETT-PACKARD"; } elsif ($ev =~ /^lexmark/) { $make = "LEXMARK"; } else { die "Could not determine printer manufacturer from \"$ev\"!\n"; } $ev_driverval = "-sDeviceManufacturer=$make -sDeviceModel=$ev"; } push (@vals, " \n", " $ev_longname\n", " $ev_shortname\n", " $ev_driverval\n", " \n", " \n", " \n", " $drvname\n", " \n", " \n", ); # This stp Model value applies only to mapped foo printers my $fooprn; for $fooprn (@{$mapstp{$ev}}) { # OK, this choice applies to this enum push (@vals, " \n", " \n", " $drvname\n", " $fooprn\n", " \n"); } push (@vals, " \n", " \n"); } return join('', "\n", @vals, " \n"); } # Stuff for Resolution. # # printer_options gives us Quality information. We examine this to # determine what to do for the gs resolution argument: # # - Is this a 180/360/720 printer or a 150/300/600 printer? # # - What are the legal resolutions? Sort of parse and compute these # from the Resolution values. # # In the case of the GhostScript driver The driverval is "X Y", and gets # passed in a /HWResolution ps clause, for the IJS driver it is "XxY" and # gets passed via the "-r" command line option of GhostScript. sub compute_resolutions { my ($stpname) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; if (!defined($rescache{$stpname})) { my @reslist = (); my %hash; my $defval; my $qual; for $qual (keys(%{$stpdata{$stpname}{'Resolution'}})) { $qual =~ m!(\d+)\s*(x\s*(\d+))?!; my ($x, $y) = ($1, $3); $y = $x if !defined($y); my $r; if ($drivertype eq "gs") { $r = {'x' => $x, 'y' => $y, 'driverval' => "$x $y", 'ev_key' => get_ev_key("res-$x-$y", $drvname) }; } else { $r = {'x' => $x, 'y' => $y, 'driverval' => "${x}x${y}", 'ev_key' => get_ev_key("res-$x-$y", $drvname) }; } push (@reslist, $r); # Default? $defval = get_ev_key("res-$x-$y", $drvname) if ($qual eq $defaults{$stpname}{'Resolution'}); # Note that this resolution value exists $resolutions{"$x $y"} = { 'x' => $x, 'y' => $y }; # Note that this printer takes this resolution $hash{$x}{$y} = 1; } $rescache{$stpname}{'list'} = \@reslist; $rescache{$stpname}{'defval'} = $defval; $rescache{$stpname}{'takesit'} = \%hash; } return $rescache{$stpname}; } sub do_all_res { my $n; for $n (keys(%mapstp)) { compute_resolutions($n); } } sub build_resolution_ev { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; my @vals = (); do_all_res(); # OK, now for each possible resolution... my $ev; for $ev (keys(%resolutions)) { my ($x, $y) = ($resolutions{$ev}{'x'}, $resolutions{$ev}{'y'}); # Put in the basic choice info: ev names, etc my $ev_longname = "$x x $y DPI"; my $ev_shortname = get_ev_shortname($ev_longname); my $ev_id = get_ev_key("res-$x-$y", $drvname); my $ev_driverval; if ($drivertype eq "gs") { $ev_driverval = "$x $y"; } else { $ev_driverval = "${x}x${y}"; } push (@vals, " \n", " $ev_longname\n", " $ev_shortname\n", " $ev_driverval\n", " \n", " \n", " \n", " $drvname\n", " \n", " \n", ); # Now, for each printer, put in a constraint if this # resolution makes sense or not... my $stpprn; for $stpprn (keys(%mapstp)) { my $resobj = compute_resolutions($stpprn); my $takesit = $resobj->{'takesit'}{$x}{$y}; if ($takesit) { my $fooprn; for $fooprn (@{$mapstp{$stpprn}}) { # print STDERR "Printer $fooprn $stpprn uses ${x}x$y\n"; # OK, this choice applies to this enum push (@vals, " \n", " $drvname\n", " $fooprn\n", " \n"); } } } push (@vals, " \n", " \n"); } return join('', "\n", @vals, " \n"); } sub build_resolution_cons { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; my @PNCONS = (); # For each stp printer... my $stpname; for $stpname (keys(%mapstp)) { # Get some resolution info my $r = compute_resolutions($stpname); # For each possible foo name my $fooname; for $fooname (@{$mapstp{$stpname}}) { # What's the default value? my $foodefval = $r->{'defval'}; push (@PNCONS, " \n", " $drvname\n", " $fooname\n", " $foodefval\n", " \n"); } } return join('', "\n", @PNCONS, " \n"); } sub build_printoutmode_ev { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; my @vals = (); # OK, now for each choice ("Draft", "Normal", ...) ... my $choice; for $choice (keys %modes) { # ... and each possible "" for it my $ev_driverval; for $ev_driverval (keys %{$modes{$choice}}) { # Put in the basic choice info: ev names, etc my $ev_longname = $printoutmodechoices->{$choice}; my $ev_shortname = $choice; my $ev_id = get_ev_key($modes{$choice}{$ev_driverval}, $drvname); push (@vals, " \n", " $ev_longname\n", " $ev_shortname\n", " $ev_driverval\n", " \n", " \n", " \n", " $drvname\n", " \n"); # Build constraints for this particular ev_driverval my $stpprn; for $stpprn (keys(%stpdata)) { my $fooprn; for $fooprn (@{$mapstp{$stpprn}}) { if ($printoutmode->{$stpprn}{$choice} eq $ev_driverval) { # OK, this choice applies to this printer push (@vals, " \n", " \n", " $drvname\n", " $fooprn\n", " \n"); } } } push (@vals, " \n", " \n"); } } return join('', "\n", @vals, " \n"); } sub build_printoutmode_cons { my ($stpopt) = @_; my $drvname = "$drivernameprefix$drivertypesuffix"; my @PNCONS = (); # For each stp printer... my $stpname; for $stpname (keys(%mapstp)) { # For each possible foo name my $fooname; for $fooname (@{$mapstp{$stpname}}) { # What's the default value (always the "Normal" mode)? my $normalmode = $printoutmode->{$stpname}{'Normal'}; my $foodefval = get_ev_key($modes{'Normal'}{$normalmode}, $drvname); push (@PNCONS, " \n", " $drvname\n", " $fooname\n", " $foodefval\n", " \n"); } } return join('', "\n", @PNCONS, " \n"); } sub qualityorder { # List of suffixes of the Quality choices my @suffixes = ( # General "", "dpi", # Epson/Lexmark "mw", "mw2", "sw", "fol", "fol2", "fourp", "uni", "mwuni", "mw2uni", "swuni", "foluni", "fol2uni", "fourpuni", "hq", "hquni", "hq2", "hq2uni", # Canon "dmt", # HP "mono", ); my ($a, $b) = @_; # Bring the suffixes to lower case my $first = lc($a); my $second = lc($b); # Check whether they are in the @suffixes list my $i; for ($i = 0; $i <= $#suffixes; $i++) { my $firstinlist = ($first eq $suffixes[$i]); my $secondinlist = ($second eq $suffixes[$i]); if (($firstinlist) && (!$secondinlist)) {return -1}; if (($secondinlist) && (!$firstinlist)) {return 1}; if (($firstinlist) && ($secondinlist)) {return 0}; } # Equal quality die "The quality choice suffixes $a and $b are unknown!\n"; return 0; } sub getprintoutmode { my $choicelongnames = { 'Draft' => 'Draft (Economy)', 'Draft.Gray' => 'Draft Grayscale (Economy)', 'Normal' => 'Normal', 'Normal.Gray' => 'Normal Grayscale', 'High' => 'High Quality', 'High.Gray' => 'High Quality Grayscale', 'VeryHigh' => 'Very High Quality', 'VeryHigh.Gray' => 'Very High Quality Grayscale', 'Photo' => 'Photo', 'Photo.Gray' => 'Photo Grayscale', }; ### BASIC RULES # See mode-specific rules below # There must be always a "Normal" mode, this will be the default. # On black-and-white printers there are no modes with ".Gray" # specifier, the standard modes are already grayscale. # No "Photo" mode on laser printers. # If on a PCL printer "600mono" is the chose quality, it will be # replaced by "300dpi" in color mode (This can lead to a mode being # removed by the following two rules). # If "VeryHigh" has exactly the same settings as "High", "VeryHigh" # is left out. # If "High" has exactly the same settings as "Normal", "High" # is left out. # If nothing is found for a certain mode, this mode is left out. my $modes = {}; # Treat all printers my $stpprn; for $stpprn (keys(%stpdata)) { my $modeinfo = {}; my ($draftminres, $draftbestsymmetry, $draftlowestqualstr) = (99999999, 99999999, "xxx"); my $normaldefaultqual = $defaults{$stpprn}{'Resolution'}; my ($highmaxres, $highbestsymmetry, $highbestqualstr) = (0, 99999999, ""); my ($veryhighmaxres, $veryhighbestsymmetry, $veryhighbestqualstr) = (0, 99999999, ""); my ($photomaxres, $photobestsymmetry, $photobestqualstr) = (0, 99999999, ""); # Go through all choices of the "Quality" option and find the # best values for the "PrintoutMode" option my $quality; for $quality (keys(%{$stpdata{$stpprn}{'Resolution'}})) { my ($xres, $yres, $qualstr); if ($quality =~ /^(\d+)x(\d+)(\D.*)$/) { $xres = $1; $yres = $2; $qualstr = $3; } elsif ($quality =~ /^(\d+)(\D.*)$/) { $xres = $1; $yres = $1; $qualstr = $2; } else { die "Invalid quality: $quality\n"; } # Resolution in dots per square inch my $respersquareinch = $xres * $yres; # Symmetry: Shows how far from symmetric a resolution is, # the smaller, the more symmetric, symmetric resolutions (as # 300x300 dpi) give zero. my $symmetry = abs(log($yres/$xres)); ### Mode: DRAFT # Use always the lowest available resolution/quality, # preferrably symmetric resolutions, # Do not use resolutions with less than 150 dpi in both # demensions. # VeryFast dithering, ImageType LineArt if (($respersquareinch < $draftminres) || (($respersquareinch == $draftminres) && ($symmetry < $draftbestsymmetry)) || (($respersquareinch == $draftminres) && ($symmetry == $draftbestsymmetry) && (qualityorder($qualstr, $draftlowestqualstr) < 0))) { $draftbestsymmetry = $symmetry; $draftminres = $respersquareinch; $draftlowestqualstr = $qualstr; next if (($xres < 150) && # Resolution not lower than ($yres < 150)); # 150x150, 360x120 allowed $modeinfo->{'Draft'} = { 'quality' => $quality, 'xres' => $xres, 'yres' => $yres, 'dither' => 'VeryFast', 'image' => 'LineArt' } } ### Mode: NORMAL # Default resolution/quality of GIMP-Print, upgrade to # unidirectional if possible, use 600x600 dpi for # Lexmark Z.. # Adaptive Hybrid dithering, ImageType Photographs if ((($stpprn =~ /^lexmark\-z/) && ($xres == 600) && ($yres == 600) && ($qualstr eq "uni")) || (($stpprn !~ /^lexmark\-z/) && (($quality eq "${normaldefaultqual}uni") || (($quality eq $normaldefaultqual) && (!defined($normal->{'quality'})))))) { $modeinfo->{'Normal'} = { 'quality' => $quality, 'xres' => $xres, 'yres' => $yres, 'dither' => 'Adaptive', 'image' => 'Photographs' } } ### Mode: HIGH # High: The highest resolution which is not higher than # 720x720 dpi (Lexmark Z..: 1200x1200 dpi), # unidirectional if possible, # not "fol", "fourp", "hq", "hq2" # Adaptive Hybrid dithering, ImageType Photographs if (($respersquareinch > $highmaxres) || (($respersquareinch == $highmaxres) && ($symmetry < $highbestsymmetry)) || (($respersquareinch == $highmaxres) && ($symmetry == $highbestsymmetry) && (qualityorder($qualstr, $highbestqualstr) > 0))) { unless ((($stpprn !~ /^lexmark\-z/) && (($xres > 720) || # Resolution not higher than ($yres > 720))) || # 720x720 for non Lexmark ($xres > 1200) || # not bigger than 1200x1200 ($yres > 1200) || # in general ($qualstr =~ /^(hq.*|fo.*)$/)) { # Not "hq", "hq2", # "fol", "fourp" $highbestsymmetry = $symmetry; $highmaxres = $respersquareinch; $highbestqualstr = $qualstr; $modeinfo->{'High'} = { 'quality' => $quality, 'xres' => $xres, 'yres' => $yres, 'dither' => 'Adaptive', 'image' => 'Photographs' } } } ### Mode: VERY HIGH # Use always the highest available resolution/quality, # preferrably symmetric resolutions, # On Epsons: Maximum 1440x720, not "hq2". # Adaptive Hybrid dithering, ImageType Photographs if (($respersquareinch > $veryhighmaxres) || (($respersquareinch == $veryhighmaxres) && ($symmetry < $veryhighbestsymmetry)) || (($respersquareinch == $veryhighmaxres) && ($symmetry == $veryhighbestsymmetry) && (qualityorder($qualstr, $veryhighbestqualstr) > 0))) { unless (($stpprn =~ /^escp2/) && # Epson (($xres > 1440) || # Resolution not higher than ($yres > 720) || # 1440x720 ($qualstr eq "hq2"))) { # Not "hq2" $veryhighbestsymmetry = $symmetry; $veryhighmaxres = $respersquareinch; $veryhighbestqualstr = $qualstr; $modeinfo->{'VeryHigh'} = { 'quality' => $quality, 'xres' => $xres, 'yres' => $yres, 'dither' => 'Adaptive', 'image' => 'Photographs' } } } ### Mode: PHOTO # Use always the highest available resolution/quality, # preferrably symmetric resolutions, # On Epsons: Maximum 2880x720 # EvenTone dithering, ImageType Photographs if (($respersquareinch > $photomaxres) || (($respersquareinch == $photomaxres) && ($symmetry < $photobestsymmetry)) || (($respersquareinch == $photomaxres) && ($symmetry == $photobestsymmetry) && (qualityorder($qualstr, $photobestqualstr) > 0))) { unless (($stpprn =~ /^escp2/) && # Epson (($xres > 2880) || # Resolution not higher than ($yres > 720))) { # 2880x720 $photobestsymmetry = $symmetry; $photomaxres = $respersquareinch; $photobestqualstr = $qualstr; $modeinfo->{'Photo'} = { 'quality' => $quality, 'xres' => $xres, 'yres' => $yres, 'dither' => 'EvenTone', 'image' => 'Photographs' } } } } # We must have a "Normal" mode for every printer. if (!defined($modeinfo->{'Normal'}{'quality'})) { die "No 'Normal' mode for $stpprn!\n"; } # Build the strings with the settings for the "PrintoutMode" # option for my $m (keys(%{$modeinfo})) { # If we didn't find anything for a certain mode, skip this # mode next if (!defined($modeinfo->{$m}{'quality'})); my $modestr = "GSResolution=$modeinfo->{$m}{'xres'}x" . "$modeinfo->{$m}{'yres'}DPI " . "Quality=$modeinfo->{$m}{'quality'} " . "Dither=$modeinfo->{$m}{'dither'} " . "ImageType=$modeinfo->{$m}{'image'}"; if (defined($stpdata{$stpprn}{'Color'}{'Color'})) { # Color printer $modes->{$stpprn}{$m} = $modestr . " OutputType=Color"; $modes->{$stpprn}{"$m.Gray"} = $modestr . " OutputType=Grayscale"; # Some HP inkjets have a "600mono" quality mode which # is only available in Grayscale, replace this mode by # "300dpi" in the settings for color printing if ($modes->{$stpprn}{$m} =~ /600mono/) { if(!defined($stpdata{$stpprn}{'Resolution'}{'300dpi'})){ die "No '300dpi' mode for $stpprn!"; } $modes->{$stpprn}{$m} =~ s/600x600DPI/300x300DPI/; $modes->{$stpprn}{$m} =~ s/600mono/300dpi/; } } else { # bw printer if ($stpprn =~ /^pcl\-[2-6][vls]?i?$/) { # Laser printer # No 'Photo' mode on laser printers next if ($m eq 'Photo'); # Always "VeryFast" dithering on laser printers $modestr =~ s/(Dither=)\S+/$1VeryFast/; } $modes->{$stpprn}{$m} = $modestr . " OutputType=Grayscale"; } } # Remove 'VeryHigh' and 'High' if they are identical to lower # quality modes if ($modes->{$stpprn}{'VeryHigh'} eq $modes->{$stpprn}{'High'}) { delete($modes->{$stpprn}{'VeryHigh'}); } if ($modes->{$stpprn}{'High'} eq $modes->{$stpprn}{'Normal'}) { delete($modes->{$stpprn}{'High'}); } if (defined($stpdata{$stpprn}{'Color'}{'Color'})) { # Color printer if ($modes->{$stpprn}{'VeryHigh.Gray'} eq $modes->{$stpprn}{'High.Gray'}) { delete($modes->{$stpprn}{'VeryHigh.Gray'}); } if ($modes->{$stpprn}{'High.Gray'} eq $modes->{$stpprn}{'Normal.Gray'}) { delete($modes->{$stpprn}{'High.Gray'}); } } } return ($modes, $choicelongnames) } exit(0);