Files
gimp/plug-ins/perl/examples/gimpmagick
1999-11-29 21:46:18 +00:00

629 lines
21 KiB
Perl

#!/usr/bin/perl
use Gimp::Feature qw(gtk perl-5.005);
use Gimp 1.06 (':auto','__','N_');
use Gimp::Fu;
use Gtk;
BEGIN { eval "use Image::Magick 1.45"; $@ and Gimp::Feature::missing ("Image::Magick version 1.45 or higher") };
$VERSION = '0.2.1';
$preview_size = 160; # max. size for image preview
# this funny little function parses the Magick.xs file
sub reparse {
my $res;
$res.="%MagickTypes = (\n";
$xs = do { local(*XS,$/); open XS,"<$_[0]" or die; <XS> };
while($xs =~ /(\w+Types)\[\]\s=\s+\{([^}]+)\}/g) {
my $name=$1;
my @vals=$2=~/"([^"]+)"/g;
shift @vals if $vals[0] eq "Undefined";
$res.=" $name => sub {\n my \$m = new Gtk::Menu;\n".
join("",map " \$m->append(new Gtk::MenuItem '$_');\n",@vals).
" my \$o = new Gtk::OptionMenu;\n".
" \$o->set_menu(\$m);\n".
" optionmenu_settext(\$o,\@_);\n".
" \$o;\n".
" },\n";
}
$res.=");\n\n";
$res.="%MagickMethods = (\n";
$xs =~ /Methods\[\]\s=\s+\{\n(.+?)\s*\};/s or die;
$methods=$1;
while($methods =~ /(?:^|\n)(\s+)\{ "([^"]+)",(?: \{ (.*?))?\s*\},(?=\n\1\{|$)/gs) {
my $method=$2;
my @args=$3=~/\{"([^"]+)", (\w+)},?/gs;
$res.=" $method => [".join(",",map "'$_'",@args)."],\n";
}
$res.=");\n";
require IO::AtomicFile;
{
local $/;
open X,"<$0" or die;
$data=<X>;
$data=~s/(?<=\n#MAGICK#\n).*/sub magick {\n$res\n}\n/s;
my $file=IO::AtomicFile->open($0,"w");
$file->print($data);
$file->close;
}
}
sub optionmenu_settext {
my ($o,$ref) = @_;
$o->signal_connect (clicked => sub {
$arg{$ref} = $_[0]->get_menu->get_active->get;
});
}
sub new_entry {
my ($re,$ref)=@_;
my $e = new Gtk::Entry;
$e->signal_connect(changed => sub {
$arg{$ref}=$e->get_text;
});
$e;
}
if ($ARGV[0] eq "--reparse") {
reparse("/root/cvt/ImageMagick-4.2.0/PerlMagick/Magick.xs");
exit;
}
&magick;
%MagickTypes = (%MagickTypes,
'StringReference' => sub { new_entry "",@_ },
'DoubleReference' => sub { new_entry '^[0-9.E+-]+$',@_ },
'IntegerReference' => sub { new_entry '^[0-9+-]+$',@_ },
'ImageReference' => sub { new Gtk::Label "not yet supported" },
);
%MagickMethods = (%MagickMethods,
);
sub check {
for(values(%MagickMethods)) {
my @a=@$_;
while(@a) {
shift @a;
my $x = shift @a;
print($x," <- does not exist\n") unless $MagickTypes{$x};
}
}
}
#check;
# read the image pixels into an imagemagick-image
sub read_pixels {
my($drawable,$im)=@_;
my $th = Gimp->tile_height;
Gimp->tile_cache_ntiles (1 + $drawable->width / Gimp->tile_width);
my $type = $drawable->type;
my $format;
$format = "RGB" if $type == RGB_IMAGE;
$format = "RGBA" if $type == RGBA_IMAGE;
$format = "GRAY" if $type == GRAY_IMAGE;
die "Indexed format and GRAYA not yet supported in GimpMagick!\n" unless $format;
my $temp = Gimp->temp_name('raw');
open TEMP,">$temp\0" or die "unable to open temporary file '$temp' for writing\n";
my ($empty,$x1,$y1,$x2,$y2) = $drawable->mask_bounds;
$x2-=$x1; $y2-=$y1;
my $region = $drawable->pixel_rgn ($x1, $y1, $x2, $y2, 0, 0);
Gimp->progress_init ("transferring image data");
for(my $y=0; $y<$y2; $y+=$th) {
# calling internal function, sorry folks!
Gimp->progress_update ($y/$y2*100);
print TEMP $region->get_rect2(0,$y,$x2,$y2-$y > $th ? $th : $y2-$y);
}
close TEMP;
$im->Set(size => $x2.'x'.$y2);
$im->Read("$format:$temp");
unlink $temp;
$format;
}
# read the image pixels back
sub write_pixels {
my($drawable,$im,$format)=@_;
my $th = Gimp->tile_height;
my $buf;
my $temp = Gimp->temp_name('raw');
$im->Write("$format:$temp");
open TEMP,"<$temp\0" or die "unable to open temporary file '$temp' for writing\n";
unlink $temp;
my ($empty,$x1,$y1,$x2,$y2) = $drawable->mask_bounds;
$x2-=$x1; $y2-=$y1;
if ($x2 ne $im->get('width') or $y2 ne $im->get('height')) {
$drawable->resize ($im->get('width','height'),0,0);
$drawable->image->selection_none;
($x1,$y1,$x2,$y2)=(0,0,$im->get('width','height'));
}
my $region = $drawable->get->pixel_rgn ($x1, $y1, $x2, $y2, 1, 1);
Gimp->progress_init ("transferring image data");
my $stride = $x2*$region->bpp;
for(my $y=0; $y<$y2; $y+=$th) {
# calling internal function, sorry folks!
Gimp->progress_update ($y/$y2*100);
read TEMP,$buf,$stride*$th;
$region->set_rect2($buf,0,$y);
}
close TEMP;
undef $region;
$drawable->merge_shadow (1);
$drawable->update ($x1, $y1, $x2, $y2);
Gimp->displays_flush;
}
sub update_preview {
my ($im,$pre)=@_;
$im=$im->clone;
while($im->get('width') > $preview_size or $im->get('height') > $preview_size) {
$im->Minify;
}
if(0==open BLOB,"-|") {
$im->Write('RGB:-');
Gimp::_exit;
}
my($w,$h)=$im->get('width','height');
$pre->size($w,$h);
for (0..$h-1) {
read BLOB,$im,$w*3;
$pre->draw_row($im,0,$_,$w);
}
close BLOB;
$pre->draw(undef);
}
# Interactively apply Image::Magick
sub gimp_magick {
my ($drawable)=@_;
Gimp::gtk_init;
# generate main window
my $im = new Image::Magick;
my $format = read_pixels ($drawable, $im);
my $w = new Gtk::Dialog;
$w->set_title ("GimpMagick! $VERSION");
my $b = new Gtk::Button "Apply";
$b->signal_connect (clicked => sub { write_pixels ($drawable, $im, $format); main_quit Gtk });
$w->action_area->add ($b);
$b = new Gtk::Button "Cancel";
$b->signal_connect (clicked => sub { main_quit Gtk });
$w->action_area->add ($b);
$preview = new Gtk::Preview "color";
$w->vbox->add ($preview);
my $frame = new Gtk::Frame "Arguments";
my $cbox = new Gtk::VBox 0,0;
$frame->add($cbox);
my $command = new Gtk::Combo;
my %args;
$command->set_popdown_strings (sort keys %MagickMethods);
$command->set_case_sensitive (0);
my $changed_command = sub {
$method = $command->entry->get_text;
return unless $MagickMethods{$method};
$frame->remove ($cbox);
$cbox = new Gtk::VBox 0,5;
my @args = @{$MagickMethods{$method}};
while(@args) {
%arg=();
my($name,$type)=(shift @args,shift @args);
my($hbox)=new Gtk::HBox 0,5;
$hbox->add(new Gtk::Label "$name: ");
my $widget = $MagickTypes{$type}->($name);
$hbox->add($widget);
$cbox->add($hbox);
}
$cbox->show_all;
$frame->add($cbox);
};
$command->entry->signal_connect(changed => $changed_command);
my $execute = new Gtk::Button "Execute!";
$execute->signal_connect(clicked => sub {
$im->$method(%arg);
update_preview ($im, $preview);
});
$w->vbox->add($command);
$w->vbox->add($frame);
$w->vbox->add($execute);
update_preview ($im, $preview);
$w->show_all;
&$changed_command;
main Gtk;
();
}
register "gimp_magick",
"access to the Image::Magick-package",
"Gimp::Magick gives you access to all methods in the Image::Magick-package. These methods often offer ".
"higher quality than equivalent Gimp methods, as well as offering more methods than Gimp itself",
"Marc Lehmann",
"Marc Lehmann",
$VERSION,
N_"<Image>/Filters/Misc/Magick",
"*",
[
],
sub {
my($image,$drawable)=@_;
gimp_magick ($drawable);
$image;
};
exit main;
#MAGICK#
sub magick {
%MagickTypes = (
BooleanTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'False');
$m->append(new Gtk::MenuItem 'True');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
ClassTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'DirectClass');
$m->append(new Gtk::MenuItem 'PseudoClass');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
ColorspaceTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'RGB');
$m->append(new Gtk::MenuItem 'Gray');
$m->append(new Gtk::MenuItem 'Transparent');
$m->append(new Gtk::MenuItem 'OHTA');
$m->append(new Gtk::MenuItem 'XYZ');
$m->append(new Gtk::MenuItem 'YCbCr');
$m->append(new Gtk::MenuItem 'YCC');
$m->append(new Gtk::MenuItem 'YIQ');
$m->append(new Gtk::MenuItem 'YPbPr');
$m->append(new Gtk::MenuItem 'YUV');
$m->append(new Gtk::MenuItem 'CMYK');
$m->append(new Gtk::MenuItem 'sRGB');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
CompositeTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Over');
$m->append(new Gtk::MenuItem 'In');
$m->append(new Gtk::MenuItem 'Out');
$m->append(new Gtk::MenuItem 'Atop');
$m->append(new Gtk::MenuItem 'Xor');
$m->append(new Gtk::MenuItem 'Plus');
$m->append(new Gtk::MenuItem 'Minus');
$m->append(new Gtk::MenuItem 'Add');
$m->append(new Gtk::MenuItem 'Subtract');
$m->append(new Gtk::MenuItem 'Difference');
$m->append(new Gtk::MenuItem 'Bumpmap');
$m->append(new Gtk::MenuItem 'Replace');
$m->append(new Gtk::MenuItem 'ReplaceRed');
$m->append(new Gtk::MenuItem 'ReplaceGreen');
$m->append(new Gtk::MenuItem 'ReplaceBlue');
$m->append(new Gtk::MenuItem 'ReplaceMatte');
$m->append(new Gtk::MenuItem 'Blend');
$m->append(new Gtk::MenuItem 'Displace');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
CompressionTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'None');
$m->append(new Gtk::MenuItem 'BZip');
$m->append(new Gtk::MenuItem 'Fax');
$m->append(new Gtk::MenuItem 'Group4');
$m->append(new Gtk::MenuItem 'JPEG');
$m->append(new Gtk::MenuItem 'LZW');
$m->append(new Gtk::MenuItem 'Runlength');
$m->append(new Gtk::MenuItem 'Zip');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
FilterTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Point');
$m->append(new Gtk::MenuItem 'Box');
$m->append(new Gtk::MenuItem 'Triangle');
$m->append(new Gtk::MenuItem 'Hermite');
$m->append(new Gtk::MenuItem 'Hanning');
$m->append(new Gtk::MenuItem 'Hamming');
$m->append(new Gtk::MenuItem 'Blackman');
$m->append(new Gtk::MenuItem 'Gaussian');
$m->append(new Gtk::MenuItem 'Quadratic');
$m->append(new Gtk::MenuItem 'Cubic');
$m->append(new Gtk::MenuItem 'Catrom');
$m->append(new Gtk::MenuItem 'Mitchell');
$m->append(new Gtk::MenuItem 'Lanczos');
$m->append(new Gtk::MenuItem 'Bessel');
$m->append(new Gtk::MenuItem 'Sinc');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
GravityTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Forget');
$m->append(new Gtk::MenuItem 'NorthWest');
$m->append(new Gtk::MenuItem 'North');
$m->append(new Gtk::MenuItem 'NorthEast');
$m->append(new Gtk::MenuItem 'West');
$m->append(new Gtk::MenuItem 'Center');
$m->append(new Gtk::MenuItem 'East');
$m->append(new Gtk::MenuItem 'SouthWest');
$m->append(new Gtk::MenuItem 'South');
$m->append(new Gtk::MenuItem 'SouthEast');
$m->append(new Gtk::MenuItem 'Static');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
ImageTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Bilevel');
$m->append(new Gtk::MenuItem 'Grayscale');
$m->append(new Gtk::MenuItem 'Palette');
$m->append(new Gtk::MenuItem 'TrueColor');
$m->append(new Gtk::MenuItem 'Matte');
$m->append(new Gtk::MenuItem 'ColorSeparation');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
IntentTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Saturation');
$m->append(new Gtk::MenuItem 'Perceptual');
$m->append(new Gtk::MenuItem 'Absolute');
$m->append(new Gtk::MenuItem 'Relative');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
InterlaceTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'None');
$m->append(new Gtk::MenuItem 'Line');
$m->append(new Gtk::MenuItem 'Plane');
$m->append(new Gtk::MenuItem 'Partition');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
LayerTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Red');
$m->append(new Gtk::MenuItem 'Green');
$m->append(new Gtk::MenuItem 'Blue');
$m->append(new Gtk::MenuItem 'Matte');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
MethodTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Point');
$m->append(new Gtk::MenuItem 'Replace');
$m->append(new Gtk::MenuItem 'Floodfill');
$m->append(new Gtk::MenuItem 'FillToBorder');
$m->append(new Gtk::MenuItem 'Reset');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
ModeTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Frame');
$m->append(new Gtk::MenuItem 'Unframe');
$m->append(new Gtk::MenuItem 'Concatenate');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
NoiseTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Uniform');
$m->append(new Gtk::MenuItem 'Gaussian');
$m->append(new Gtk::MenuItem 'Multiplicative');
$m->append(new Gtk::MenuItem 'Impulse');
$m->append(new Gtk::MenuItem 'Laplacian');
$m->append(new Gtk::MenuItem 'Poisson');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
PreviewTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Rotate');
$m->append(new Gtk::MenuItem 'Shear');
$m->append(new Gtk::MenuItem 'Roll');
$m->append(new Gtk::MenuItem 'Hue');
$m->append(new Gtk::MenuItem 'Saturation');
$m->append(new Gtk::MenuItem 'Brightness');
$m->append(new Gtk::MenuItem 'Gamma');
$m->append(new Gtk::MenuItem 'Spiff');
$m->append(new Gtk::MenuItem 'Dull');
$m->append(new Gtk::MenuItem 'Grayscale');
$m->append(new Gtk::MenuItem 'Quantize');
$m->append(new Gtk::MenuItem 'Despeckle');
$m->append(new Gtk::MenuItem 'ReduceNoise');
$m->append(new Gtk::MenuItem 'AddNoise');
$m->append(new Gtk::MenuItem 'Sharpen');
$m->append(new Gtk::MenuItem 'Blur');
$m->append(new Gtk::MenuItem 'Threshold');
$m->append(new Gtk::MenuItem 'EdgeDetect');
$m->append(new Gtk::MenuItem 'Spread');
$m->append(new Gtk::MenuItem 'Solarize');
$m->append(new Gtk::MenuItem 'Shade');
$m->append(new Gtk::MenuItem 'Raise');
$m->append(new Gtk::MenuItem 'Segment');
$m->append(new Gtk::MenuItem 'Swirl');
$m->append(new Gtk::MenuItem 'Implode');
$m->append(new Gtk::MenuItem 'Wave');
$m->append(new Gtk::MenuItem 'OilPaint');
$m->append(new Gtk::MenuItem 'Charcoal');
$m->append(new Gtk::MenuItem 'JPEG');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
PrimitiveTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'Point');
$m->append(new Gtk::MenuItem 'Line');
$m->append(new Gtk::MenuItem 'Rectangle');
$m->append(new Gtk::MenuItem 'FillRectangle');
$m->append(new Gtk::MenuItem 'Circle');
$m->append(new Gtk::MenuItem 'FillCircle');
$m->append(new Gtk::MenuItem 'Ellipse');
$m->append(new Gtk::MenuItem 'FillEllipse');
$m->append(new Gtk::MenuItem 'Polygon');
$m->append(new Gtk::MenuItem 'FillPolygon');
$m->append(new Gtk::MenuItem 'Color');
$m->append(new Gtk::MenuItem 'Matte');
$m->append(new Gtk::MenuItem 'Text');
$m->append(new Gtk::MenuItem 'Image');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
ResolutionTypes => sub {
my $m = new Gtk::Menu;
$m->append(new Gtk::MenuItem 'PixelsPerInch');
$m->append(new Gtk::MenuItem 'PixelsPerCentimeter');
my $o = new Gtk::OptionMenu;
$o->set_menu($m);
optionmenu_settext($o,@_);
$o;
},
);
%MagickMethods = (
Comment => ['comment','StringReference'],
Label => ['label','StringReference'],
AddNoise => ['noise','NoiseTypes'],
Colorize => ['color','StringReference','pen','StringReference'],
Border => ['geom','StringReference','width','IntegerReference','height','IntegerReference','color','StringReference'],
Blur => ['factor','DoubleReference'],
Chop => ['geom','StringReference','width','IntegerReference','height','IntegerReference','x','IntegerReference','y','IntegerReference'],
Crop => ['geom','StringReference','width','IntegerReference','height','IntegerReference','x','IntegerReference','y','IntegerReference'],
Despeckle => [],
Edge => ['factor','DoubleReference'],
Emboss => [],
Enhance => [],
Flip => [],
Flop => [],
Frame => ['geom','StringReference','width','IntegerReference','height','IntegerReference','inner','IntegerReference','outer','IntegerReference','color','StringReference'],
Implode => ['factor','DoubleReference'],
Magnify => [],
MedianFilter => [],
Minify => [],
OilPaint => ['radius','IntegerReference'],
ReduceNoise => [],
Roll => ['geom','StringReference','x','IntegerReference','y','IntegerReference'],
Rotate => ['degree','DoubleReference','crop','BooleanTypes','sharpen','BooleanTypes'],
Sample => ['geom','StringReference','width','IntegerReference','height','IntegerReference'],
Scale => ['geom','StringReference','width','IntegerReference','height','IntegerReference'],
Shade => ['geom','StringReference','azimuth','DoubleReference','elevat','DoubleReference','color','BooleanTypes'],
Sharpen => ['factor','DoubleReference'],
Shear => ['geom','StringReference','x','DoubleReference','y','DoubleReference','crop','BooleanTypes'],
Spread => ['amount','IntegerReference'],
Swirl => ['degree','DoubleReference'],
Zoom => ['geom','StringReference','width','IntegerReference','height','IntegerReference','filter','FilterTypes'],
IsGrayImage => [],
Annotate => ['text','StringReference','font','StringReference','point','IntegerReference','density','StringReference','box','StringReference','pen','StringReference','geom','StringReference','server','StringReference','x','IntegerReference','y','IntegerReference','grav','GravityTypes'],
ColorFloodfill => ['geom','StringReference','x','IntegerReference','y','IntegerReference','pen','StringReference','bordercolor','StringReference'],
Composite => ['compos','CompositeTypes','image','ImageReference','geom','StringReference','x','IntegerReference','y','IntegerReference','grav','GravityTypes'],
Contrast => ['sharp','BooleanTypes'],
CycleColormap => ['amount','IntegerReference'],
Draw => ['prim','PrimitiveTypes','points','StringReference','meth','MethodTypes','pen','StringReference','linew','IntegerReference','server','StringReference','borderc','StringReference'],
Equalize => [],
Gamma => ['gamma','StringReference','red','DoubleReference','green','DoubleReference','blue','DoubleReference'],
Map => ['image','ImageReference','dither','BooleanTypes'],
MatteFloodfill => ['geom','StringReference','x','IntegerReference','y','IntegerReference','matte','IntegerReference','bordercolor','StringReference'],
Modulate => ['factor','StringReference','bright','DoubleReference','satur','DoubleReference','hue','DoubleReference'],
Negate => ['gray','BooleanTypes'],
Normalize => [],
NumberColors => [],
Opaque => ['color','StringReference','pen','StringReference'],
Quantize => ['colors','IntegerReference','tree','IntegerReference','colorsp','ColorspaceTypes','dither','BooleanTypes','measure','BooleanTypes','global','BooleanTypes'],
Raise => ['geom','StringReference','width','IntegerReference','height','IntegerReference','x','IntegerReference','y','IntegerReference','raise','BooleanTypes'],
Segment => ['colorsp','ColorspaceTypes','verbose','BooleanTypes','clust','DoubleReference','smooth','DoubleReference'],
Signature => [],
Solarize => ['factor','DoubleReference'],
Sync => [],
Texture => ['texture','ImageReference'],
Transform => ['crop','StringReference','geom','StringReference','filter','FilterTypes'],
Transparent => ['color','StringReference'],
Threshold => ['threshold','DoubleReference'],
Charcoal => ['factor','StringReference'],
Trim => [],
Wave => ['geom','StringReference','ampli','DoubleReference','wave','DoubleReference'],
Layer => ['layer','LayerTypes'],
Condense => [],
Stereo => ['image','ImageReference'],
Stegano => ['image','ImageReference','offset','IntegerReference'],
Coalesce => [],
);
}