#!/usr/bin/perl -w
# gstoraster	CUPS filter to convert PostScript or PDF to cups raster format
# ----------------------------------------------------------------------------
# Copyright 2010 Helge Blischke
# 1.00 - 2010-12-15/Bl
#	initial implementation
# 1.01 - 2011-05-14/Bl
#	Insert bounding box comment into probe PostScript program
# 1.02 - 2011-05-16/Bl
#	Fixed dumping PostScript strings in dump_obj
#	Fixed landscape handling bug
#	Added "-dNOMEDIAATTRS" to Ghostscript options
# 1.03 - 2011-05-20/Bl
#	Preserve pure CMYK black.
#	Ghostscript 9.xy and higher do all color handling through ICC profiles,
#	and the default CMKY profile used for /DeviceCMYK colorspace does not
#	preserve pure black (at least for 9.00 to 9.02), and this is dicussed
#	controversely among ghe Ghostscript developers.
#	We therefore transform pure CMYK black to an equivalent gray value
#	if the used PPD specifies 
#	*ColorDevice: False
# 1.04 - 2011-08-02/Bl
#	Sanitize command line options of type string if they contain spaces
#	for pstops | gs call.
# 1.05 - 2013-03-10/Bl
#	Bug fix with evaluation of /cupsInteger0 and similar names.
# 1.06 - 2015-02-22/Bl
#	Process /BeginPage and /EndPage setpagedevice keys.
# 1.10 - 2016-02-26/Bl
#	Process *cupsICCProfile and *cupsICCQualifier. PPD statements
# 1.20 - 2016-12-16/Bk
#	Added support of pwgraster format; removed the "-dNOMEDIAATTRS" when using the pwgraster device.
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2 of the License, or (at your
#  option) any later version.
#
# Description
# -----------
#	This filter is designed to replace both the pstoraster and the pdftoraster
#	filter. It operates in two steps:
#	(1) A probe PostScript program is fed through the standard pstops filter to 
#	    collect all the features stemming from the PPD defaults and job attributes
#	    referring to PPD choices. The output from the pstops filter is then processed
#	    by Ghostscript to set up command line options and a page device dictionary for
#	    the second step, in which the actual input file is processed.
#	(2) Ghostscript is then called with the command line options established by (1) above
#	    and processes 
#	    a)  a special initialization file which sets up the pagedevice parameters set up
#		by (1) above and redefines some procedures/operators (namely, setpagedevice
#		to handle landscape vs portrait oriented pages (especially if the input is PDF)
#		and graphicsbeginpage to cancel the page content clipping done by ghostscript's
#		PDF interpreter), and, as of version 1.03, redefines setcolorspace, setcolor,
#		and setcmykcolor render cmyk colors, the only nonzero component is black, as
#		the equivalent gray value. This is a workaround to the weird ICC profile used
#		by Ghostscript >= 9.00 for /DeviceCMYK color space, iff the used PPF specifies
#		 *ColorDevice: False,
#	    b)	the original input file.
#	    The output is written to stdout as usual.
#	This filter honours the *LandscapeOrientation: Plus90 | Minus90 | Any
#	PPD directive (Any interpreted as Plus90) and does the necessary transformation
#	by ordinary PostScript means, thus bypassing the special CTM handling by Ghostscript's
#	cups device (which is equivalent to Minus90).
#
# Restrictions:
# -------------
#	Currently, only the Landscape/Portrait orientation is handled, forcing the output to
#	the orientation specified by the PPD default page size. Any fit-to-page scaling is not
#	yet implemented.
#
use File::Copy;
use File::Basename;

# -------------------------- configuration parameters --------------------------
@paths = ('/bin', '/usr/bin', '/usr/local/bin', '/opt/bin');
$add_to_path = ":/usr/local/bin:/opt/bin";	# add to PATH environment variable
$cmdlpgsz = 1;			# set to 1 in order to set page size by commandline option
$ignore_gserror = 0;		# set to 1 in order to ignore Ghostscript errors
$gsname = 'gs';			# the plain name of the Ghostscript executable
$psicc = 'psicc';		# the plain name of the icc to postscript converter
$log = 1;			# set to 0 to suppress NOTICE messages
# ------------------------------------------------------------------------------
$filterdir = $ENV{CUPS_SERVERBIN};
if (! defined $filterdir || ! $filterdir)
{
	chomp ($filterdir = `cups-config --serverbin`);
}
$filterdir .= "/filter";
$datadir = $ENV{CUPS_DATADIR};
if (! defined $datadir || ! $datadir)
{
	chomp ($datadir = `cups-config --datadir`);
}
#@args = @ARGV[0 .. 3];		# save argument for calling pstops filter (whithout filename)
if (scalar @ARGV != 5 && scalar @ARGV != 6)
{
	warn "ERROR: gstoraster jobid username jobname copies options [filename]\n";
	exit 1;
}
$job_id = $username = $job_name = $num_copies = $filename = undef;	# prevent complaints
$job_id = shift;
$username = shift;
$job_name = shift;
$num_copies = shift;
$options = shift;
$filename = shift;		# optional file name
$optstr = ' ' . $options . ' ';	# parsing aid
#$gs = lookup ($gsname);
# Instead of using our own lookup function, we extend the PATH environment variable
# accordingly. THis is to overcome OS X 10.x restrictions imposed by "Sandboxing strict".
$path = $ENV{PATH} . $add_to_path;
$ENV{PATH} = $path;
$gs = $gsname;
$pstops = (-f "$filterdir/pstops" && -x "$filterdir/pstops") ? "$filterdir/pstops" : undef;
if (! defined $gs)
{
	warn "ERROR: Ghostscript not found\n";
	exit 1;
}
# Check if we are to output pwgraster
@temp = `$gs -h`; chomp @temp; $gs_h = ' ' . join (' ', @temp) . ' ';
$progname = $0;
$gen_pwgraster = ($progname =~ /pwgraster$/ && $gs_h =~ /\s+pwgraster\s+/) ? 1 : undef;
chomp ($gsvers = `$gs --version`); 
if (! defined $pstops)
{
	warn "ERROR: cups filter \"pstops\" not found\n";
	exit 1;
}
$landscape = 'Plus90';			# the default
if ($optstr =~ /\s+iccprofile=(\S+)/)	# allow for filenames containing spaces
{
	if (substr ($1, 0, 1) eq '"')
	{
		if ($optstr =~ /\s+iccprofile=\"([^\"]+)\"\s+/)
		{
			$iccprofile = $1;
		}
		else
		{
			warn "ERROR: Syntax error after iccprofile= in \"$optstr\"\n";
		}
	}
	else
	{
		$iccprofile = $1;
	}
}
		
$myppd = $ENV{PPD};
if (! defined $myppd || ! $myppd)
{
	warn "ERROR: no PPD path provided\n";
	exit 1;
}
if (open (PPD, "$myppd"))
{
	my @ppd = <PPD>;
	close (PPD);
	chomp @ppd;
	($landscape) = grep (/^\*LandscapeOrientation:/, @ppd);
	$landscape = "" if (! defined $landscape);
	$landscape = ($landscape =~/^\*LandscapeOrientation:\s+(Plus90|Minus90)/) ? $1 : 'Plus90';
	# Check if the destination is a black/white device (this is a preliminary hack)
	$is_b_w = grep (/^\*ColorDevice:\s+False/, @ppd);
	$iccprofile = find_profile (@ppd) unless (defined $iccprofile && $iccprofile);
	if ($iccprofile && substr ($iccprofile, 0, 1) ne '/')
	{
		$iccprofile = "$datadir/$iccprofile";
	}
}
else
{
	warn "ERROR: $myppd: $!\n";
	exit 1;
}
$requestdir = $ENV{CUPS_REQUEST_ROOT};
$requestdir = $ENV{REQUEST_ROOT} if (! defined $requestdir || ! $requestdir);	# in case of CUPS 1.1.x
$tempdir = (defined $requestdir && $requestdir) ? "$requestdir/tmp" : "/var/tmp";


sub lookup
{
	my ($name) = @_;
	my $path = undef;
	foreach my $item (@paths)
	{
		my $temp = "$item/$name";
		if (-f $temp && -x $temp)
		{
			$path = $temp;
			last;
		}
	}
	$path;
}

sub sanitize
{
	my ($string) = @_;
	$string =~ s/ /\\ /g;
	$string;
}

# Set up the sanitized command line options for calling the pstops filter by escaping
# space characters in string type options.
@args = ($job_id);
push @args, sanitize ($username);
push @args, sanitize ($job_name);
push @args, $num_copies;
# Force the options to be interpreted by the shell as only one parameter if not empty!
push @args, ($options ne '') ? "\"$options\"" : "number-up=1";	# options must not be empty (shell!)

# Feed the probe PostScript job through pstops to get the Ghostscript command line options
# and the page device statements for the final conversion

$psprobe = '%!PS-Adobe-3.0
%%Title: probe file to get the page device statements and Ghostscript command line options.
%%BoundingBox: (atend)
%%Pages: 0
%%EndComments
%%BeginProlog
% In this probe PostScript program, we redefine setpagedevice and currentpagedevice as local
% procedures used only to collect the parameters, as tests have shown that (at least) 
% espgs 815.02 does not honour the /PageSize key with the cups device.
/FAKEdict 16 dict def
currentpagedevice FAKEdict copy pop	% copy the current page device
/setpagedevice{FAKEdict copy userdict/FAKEdict 3 -1 roll put}bind def
/currentpagedevice{FAKEdict dup length dict copy}bind def

/resstr 0 string def		% the result string
/write_str			% <string> <nl_or_not> write_str -
{
	{(\n)concatstrings}if
	resstr exch concatstrings /resstr exch def
}bind def

/dump_obj			% <collected-str> <any> dump_obj <updated-str>
{
	dup type /arraytype eq
	{dump_array}
	{dup type /dicttype eq
		{dump_dict}
		{dup type /nametype eq
			{dup xcheck not exch =string cvs exch{(/)exch concatstrings}if}
			{dup type /stringtype ne
				{=string cvs}
				{(\()exch concatstrings(\))concatstrings}ifelse
			}ifelse
			1 index length 0 ne
			{exch ( )concatstrings exch}if
			concatstrings
		}ifelse
	}ifelse
}bind def

/dump_array
{	% account for executable array (procedures) to handle /BeginPage and /EndPage
	dup xcheck
	{
		exch ( {)concatstrings exch
		{dump_obj}forall
		( })concatstrings
	}
	{
		exch ( [)concatstrings exch
		{dump_obj}forall
		( ])concatstrings
	}ifelse
}bind def


/dump_dict
{
	exch (<<)concatstrings exch
	{3 1 roll dump_obj exch dump_obj}forall
	( >>)concatstrings
}bind def

/barf	% <value> <keyword> barf -
{
	1 index type /stringtype eq
	{1 index length 0 ne{true}{false}ifelse}
	{1 index dup type /integertype eq exch type /realtype eq or
		{1 index 0 ne{true}{false}ifelse}
		{1 index null ne{true}{false}ifelse}ifelse
	}ifelse
	{
		() exch dump_obj exch dump_obj true write_str
	}
	{pop pop}ifelse
}bind def

/belch	% <number> belch <string>
{
	dup type /realtype eq{0.5 add cvi}if 16 string cvs
}bind def
%%EndProlog
%%BeginSetup
% Evaluate command line options for Ghostscript from page device parameters
%currentpagedevice{exch ==only ( )print ==}forall flush
currentpagedevice
dup/BeginPage  .knownget{/BeginPage barf}if
dup/EndPage    .knownget{/EndPage barf}if
dup/MediaClass .knownget{/MediaClass barf}if
dup/MediaColor .knownget{/MediaColor barf}if
dup/MediaType  .knownget{/MediaType barf}if
dup/OutputType .knownget{/OutputType barf}if
dup/AdvanceDistance .knownget{/AdvanceDistance barf}if
dup/AdvanceMedia .knownget{/AdvanceMedia barf}if
dup/Collate .knownget{/Collate barf}if
dup/CutMedia .knownget{/CutMedia barf}if
dup/Duplex .knownget{/Duplex barf}if
dup/HWResolution .knownget
	{
		aload pop belch exch belch
		(-r)exch concatstrings(x)concatstrings exch concatstrings
		(\n)concatstrings print flush
	}if
dup/InsertSheet .knownget{/InsertSheet barf}if
dup/Jog .knownget{/Jog barf}if
dup/LeadingEdge .knownget{/LeadingEdge barf}if
dup/ManualFeed .knownget{/ManualFeed barf}if
dup/MediaPosition .knownget{/MediaPosition barf}if
dup/MediaWeight .knownget{/MediaWeight barf}if
dup/MirrorPrint .knownget{/MirrorPrint barf}if
dup/NegativePrint .knownget{/NegativePrint barf}if
dup/NumCopies .knownget{dup 1 ne{/NumCopies barf}{pop}ifelse}if
dup/Orientation .knownget{/Orientation barf}if
dup/OutputFaceUp .knownget{/OutputFaceUp barf}if
/PGSZ where not
{
	dup/PageSize .knownget{/PageSize barf}if	% set page size per setpagedevice
}
{
	pop						% pop the dict where PGSZ is defined
	dup/PageSize .knownget				% set page size per command line options
	{
		aload pop belch exch belch		% force rounded integer decimals
		(-dDEVICEWIDTHPOINTS=) exch concatstrings true write_str
		(-dDEVICEHEIGHTPOINTS=) exch concatstrings true write_str
	}if
}ifelse
dup/Separations .knownget{/Separations barf}if
dup/TraySwitch .knownget{/TraySwitch barf}if
dup/Tumble .knownget{/Tumble barf}if
dup/cupsMediaType .knownget{/cupsMediaType barf}if
dup/cupsBitsPerColor .knownget{/cupsBitsPerColor barf}if
dup/cupsColorOrder .knownget{/cupsColorOrder barf}if
dup/cupsColorSpace .knownget{/cupsColorSpace barf}if
dup/cupsCompression .knownget{/cupsCompression barf}if
dup/cupsRowCount .knownget{/cupsRowCount barf}if
dup/cupsRowFeed .knownget{/cupsRowFeed barf}if
dup/cupsRowStep .knownget{/cupsRowStep barf}if
dup/cupsBorderlessScalingFactor .knownget{/cupsBorderlessScalingFactor barf}if
(cupsInteger)
0 1 15{
	2 string cvs 1 index exch concatstrings
	2 index 1 index .knownget
	{exch cvn barf}
	{pop}ifelse
}for
pop (cupsReal)
0 1 15{
	2 string cvs 1 index exch concatstrings
	2 index 1 index .knownget
	{exch cvn barf}
	{pop}ifelse
}for
pop (cupsString)
0 1 15{
	2 string cvs 1 index exch concatstrings
	2 index 1 index .knownget
	{exch cvn barf}
	{pop}ifelse
}for
pop
dup/cupsMarkerType .knownget{/cupsMarkerType barf}if
dup/cupsRenderingIntent .knownget{/cupsRenderingIntent barf}if
dup/cupsPageSizeName .knownget{/cupsPageSizeName barf}if
pop
/resstr load print flush
%%EndSetup
%%EOF';

# We use -sDEVICE=cups / pwgraster to guarantee that Ghostscript does not refuse any of the page device
# parameters specified by the PPD. As the probe file contains no page(s), no output is written.
# In case $cmdlpgsz is defined and not null, force the page size to be passed as commandline
# options, not as setpagedevice statement.
$gsdevice = (defined $gen_pwgraster && $gen_pwgraster) ? "pwgraster" : "cups";
$opts = (defined $cmdlpgsz && $cmdlpgsz) ? '-dPGSZ' : '';
$pstopscmd = "echo \"$psprobe\" | $pstops " . join (' ', @args) . " | $gs -q $opts -sDEVICE=$gsdevice -f -_ -c quit";
# warn "$pstopscmd\n";
@results = `$pstopscmd 2>&1`;
# warn @results;
chomp @results;
$gssetpg = "";					# holds the setpagedevice parameters
@gsopts = (
	"$gs",
	"-q",
	"-dREALLYDELAYBIND",
	"-dNOPAUSE",
	"-dBATCH",
	"-sPPD_LANDSCAPE=$landscape",		# initial value from PPD
	"-sDEVICE=$gsdevice",
	'-sstdout=%stderr',
	'-sOutputFile=%stdout',
	);
push @gsopts, "-dNOMEDIAATTRS" if ($gsdevice eq "cups");
push @gsopts, "-dB_W_DEVICE" if (defined $is_b_w && $is_b_w);	# pass black/white device flag to Ghostscript
$linepref = 'DEBUG:';
foreach my $line (@results)
{
	next if ($line =~ /--nostringval--/);
	warn "NOTICE:$line\n" if (defined $log && $log);
	$line =~ s/^\000//;
	if ($line =~ /^-/)			# Ghostscript option from pagedevice
	{
		push @gsopts, $line;
	}
	elsif ($line =~ /^\//)
	{
		$gssetpg .= ($gssetpg eq "") ? "<<$line" : " $line";
	}
	elsif ($line =~ /^(DEBUG:|NOTICE:|WARNING:|ERROR:|INFO:)/)
	{
		warn "$line\n";
	}
	else
	{
		$linepref = 'ERROR:' if ($line =~ /Error:/);	# Ghostscript error
		warn "$linepref$line\n";
	}
}
if ($gssetpg)
{
	if ($optstr =~ /\s+profile=(\S+)/)
	{
		my $profile = "/cupsProfile ($1)";
		warn "NOTICE:$profile\n" if (defined $log && $log);
		$gssetpg .= " $profile";
	}
	$gssetpg .= ">>setpagedevice\n";
	# warn "NOTICE:$gssetpg\n";
}

# foreach my $line (@gsopts){warn "$line\n";}
#
# If we have an ICC profile, process it as follows:
# If the Ghostscript version is less than 9.0, convert the ICC profile to a 
# PostScript CRD (Color Rendering Dictionary).
# Otherwise, as Ghostscript 9.0 and higher ignore CRDs (there is a change request
# to process CRDs again, but ...), ell Ghostscript to use the ICC profile as an
# output profile by command line option.
if (defined $iccprofile && $iccprofile)
{
	if ($gsvers < 9.0)
	{
		my ($temp, $intent) = ('', 0);
		if (open (ICC, "<$iccprofile"))
		{
			seek (ICC, 67, 0);
			read (ICC, $temp, 1);
			$intent = ord ($temp) if ($temp);
			close (ICC);
		}
		$pscrd = `$psicc -o \"$iccprofile\" -u -t $intent`;
		chomp $pscrd;
		$pscrd .= "setcolorrendering\n";
	}
	else
	{
		$icctemp = "$tempdir/" . basename ($iccprofile);
		$icctemp =~ s/ /_/g;	# replace spaces
		copy ($iccprofile, "$icctemp");
		push @gsopts, "-sOutputICCProfile=$icctemp";
	}
}
warn "NOTICE: ", join (' ', @gsopts), "\n";
#
# Now set up what we need to process the input, especially in case of PDF
#
$gsprolog = "%%BeginFile: gs_prolog 1.0 1
%%Creator: Helge Blischke
%%CreationDate: 2015-02-22
% NOTE: Ghostscript needs to be called with the option -DELAYBIND
% Save the original pagedevice operators
/sys_setpagedevice /setpagedevice load def
/sys_currentpagedevice /currentpagedevice load def
currentglobal true setglobal
GS_PDF_ProcSet /sys_graphicsbeginpage GS_PDF_ProcSet/graphicsbeginpage get .forceput
setglobal
/B_W_DEVICE dup where{exch get}{pop false}ifelse
{
	/sys_setcolorspace /setcolorspace load def
	/sys_setcolor /setcolor load def
	/sys_setcmykcolor /setcmykcolor load def
	userdict/is_cmyk false put	% initialization
	/setcolorspace
	{
		sys_setcolorspace
		currentcolorspace 0 get /DeviceCMYK eq
		userdict/is_cmyk 3 -1 roll put
	}bind def
	/setcolor
	{
		userdict/is_cmyk get not
		{sys_setcolor}
		{
			4 copy pop	% c m y
			0 eq exch 0 eq and exch 0 eq and
			{1 exch sub setgray 3{pop}repeat}
			{/DeviceCMYK sys_setcolorspace sys_setcolor}ifelse
		}ifelse
	}bind def
	/setcmykcolor
	{
		4 copy pop      % c m y
		0 eq exch 0 eq and exch 0 eq and
		{1 exch sub setgray 3{pop}repeat}
		{sys_setcmykcolor}ifelse
	}bind def
}if
% Set up the final page device dictionary
$gssetpg
% Save page device properties we need to compute rotating and scaling
/sys_PageSize sys_currentpagedevice/PageSize get def
/Portrait? sys_PageSize aload pop le def
/Plus90 {90 rotate 0 //sys_PageSize 0 get neg translate} def
/Minus90 {90 neg rotate //sys_PageSize 1 get neg 0 translate} def
% Define our landscape orientation, depending on PPD_LANDSCAPE:
/Landscape /PPD_LANDSCAPE dup where{exch get}{pop (Plus90)}ifelse (Plus90) eq {//Plus90}{//Minus90}ifelse def
% Set up the fake operators
currentglobal true setglobal
GS_PDF_ProcSet
/graphicsbeginpage
{
	currentdict/ClipRect undef	% ClipRect stems from /CropBox, if present
	sys_graphicsbeginpage
}bind .forceput
setglobal
/setpagedevice
{
	% Currently, we do not implement any scale to fit operation as we suppose
	% the page size(s) specified by the input data only differ from the PPD default
	% by the orientation.
	dup/PageSize .knownget
	{
		aload pop le Portrait? xor
		{
			dup/BeginPage .knownget
			{cvlit [ exch aload pop //Landscape aload pop ] cvx}
			{pop //Landscape exec}ifelse
			dup/BeginPage exch put
		}if
		dup/PageSize undef				% remove /PageSize key - value pair
	}if
	dup/Orientation undef
%	dup/Install undef
	sys_setpagedevice					% do not ignore the rest
}bind def

% As we need to intercept the setpagedevice operator even when processing PDF, we call
% Ghostscript with the DELAYBIND option (because of setpagedevice called in pdfshowpage)
% and now can perform the bind safely.
.bindnow
";

# As we must fed two different files (the above prolog and the original input) into Ghostscript,
# and we cannot do both from Ghostscript's stdin (the original input may be PDF), we save both 
# to a temporary file in CUPS' tmp directory.

$prolfile = "$tempdir/gsprolog.$$.ps";
if (open (TMP, ">$prolfile"))
{
	print TMP "$gsprolog";
	print TMP "$pscrd" if (defined $pscrd && $pscrd);
	print TMP "%%EndFile\n"; 
	close (TMP);
}
else
{
	warn "ERROR: $prolfile: $!\n";
	exit 1;
}
if (! defined $filename || ! $filename)
{						# input is from stdin
	$tempfile = "$tempdir/gsinput.$$.tmp";
	copy (\*STDIN, $tempfile);
	if ($!)
	{
		warn "ERROR: $tempfile: $!\n";
		exit 1;
	}
	$gsinput = $tempfile;
}
else
{
	$gsinput = $filename;
}

#push @gsopts, "-c save pop";
push @gsopts, "-f";
push @gsopts, "$prolfile";
push @gsopts, "$gsinput";
push @gsopts, "-c quit";

# warn "NOTICE: ", join (' ', @gsopts), "\n";
system @gsopts;
if ($?)
{
	warn "ERROR: Ghostscript exited with value ", $? >> 8, "\n";
	exit 1 unless (defined $ignore_gserror && $ignore_gserror);
}
unlink $prolfile;
unlink "$icctemp" if (defined $icctemp && $icctemp);
unlink $tempfile if ($tempfile);
exit 0;

sub find_profile
{
	my @ppd = @_;
	my ($key, $val, $ix, $i);
	my $result = "";
	my @profiles = grep (/^\*cupsICCProfile/, @ppd);
	my @qualifiers = grep (/^\*cupsICCQualifier/, @ppd);
	my @iccqualifiers = ("ColorSpace", "ColorModel", "MediaType", "Resolution");
	my @iccvalues = ("", "", "", "");
	foreach my $quali (@qualifiers)
	{
		if ($quali =~ /Qualifier(\d):\s+(\S+)/)
		{
			$ix = $1;
			$val = $2;
			$iccqualifiers[$ix] = $val;
		}
	}
	for $i (1, 0, 2, 3)
	{
		$key = $iccqualifiers[$i];
		if ($optstr && $optstr =~ /\s+$key=(\S+)\s+/i)
		{
			$val = $1;
		}
		else
		{
			my ($temp) = grep (/^\*Default$key:\s+(\S+)/, @ppd);
			$val = ($temp && $temp =~ /^\*Default$key:\s+(\S+)/) ? $1 : "dummy";
		}
		$iccvalues[$i] = $val;
	}
	foreach my $test (@profiles)
	{
		$test =~ s/\*cupsICCProfile\s+//;
		if ($test =~ /^([^\.]*)\.([^\.]*)\.([^\/]*)\//)
		{
			my ($v1, $v2, $v3) = ($1, $2, $3);
			if (!$v1 || $v1 eq (($iccvalues[1]) ? $iccvalues[1] : $iccvalues[0]))
			{if (!$v2 || $v2 eq $iccvalues[2])
			 {if (!$v3 || $v3 eq $iccvalues[3])
			  {
				$test =~ s/^[^:]+:\s+\"//;
				$test =~ s/\".*$//;
				$result = $test;
				last;
			  }
			 }
			}
		}
	}
	$result;
}	

