1/*
2
3   ImageMagick operations edited by Alan Gibson (aka "snibgo"; snibgo at earthling dot net).
4
5   1-Apr-2014
6     Minor corrections to Geometry_widget and Alpha.
7     Added loads of widgets and Menuactions.
8     Not fully tested.
9   5-Apr-2014
10     Many more menu actions.
11     Reorganised Magick menu.
12   10-Apr-2014
13     Many more menu actions.
14   11-Apr-2014 jcupitt
15     Split to separate _magick.def
16	 Add 0-ary and 2-ary system
17	 Put utility funcs into a Magick class
18   11-Apr-2014 snibgo
19     Added VirtualPixelBack for cases where background is only relevant when VP=Background
20   17-Apr-2014 snibgo
21     Many small changes.
22   2-May-2014 jcupitt
23     Added Magick.version
24   30-June-2014
25   	 Put single-quotes around command exe to help win
26   1-July-2014
27     Automatically fall back to gm if we can't find convert
28   17-July-2014
29     better GM support
30
31
32   Last update: 17-July-2014.
33
34   For details of ImageMagick operations, see http://www.imagemagick.org/script/command-line-options.php etc.
35
36*/
37
38/* Put these in a class to avoid filling the main namespace with IM stuff.
39 */
40
41Magick = class {
42
43	// first gm on path, or ""
44	gm_path = search_for "gm";
45
46	// first convert on $PATH, or ""
47	// we check for the convert we ship first
48	convert_path
49		= vips_convert, vips_convert != ""
50		= search_for "convert"
51	{
52		// the convert we ship with the vips binary on some platforms, or ""
53		vips_convert
54			= search (path_absolute convert)
55		{
56			vipshome = path_parse (expand "$VIPSHOME");
57			convert = vipshome ++ ["bin", "convert" ++ expand "$EXEEXT"];
58		}
59	}
60
61	use_gm_pref = Workspaces.Preferences.USE_GRAPHICSMAGICK;
62
63	// Are we in GM or IM mode?
64	use_gm
65		= true, use_gm_pref && gm_path != ""
66		= false, !use_gm_pref && convert_path != ""
67		= false, convert_path != ""
68		= true, gm_path != ""
69		= error "neither IM nor GM executable found";
70
71	command_path
72		= gm_path, use_gm
73		= convert_path;
74
75	// try to get the version as eg. [6, 7, 7, 10]
76	// GM versions are smaller, typically [1, 3, 18]
77	version
78		= map parse_int (split (member ".-") version_string)
79	{
80		[output] = vips_call "system"
81			["'" ++ command_path ++ "' -version"] [$log=>true];
82		version_string
83			= (split (equal ' ') output)?1, use_gm
84			= (split (equal ' ') output)?2;
85	}
86
87	// make a command-line ... args is a [str] we join with spaces
88	command args
89		= "'" ++ command_path ++ "' " ++ join_sep " " args'
90	{
91		args'
92			= ["convert"] ++ args, use_gm
93			= args;
94	}
95
96	// capabilities ... different versions support different features, we
97	// turn features on and off based on these
98
99	// would probably be better to test for caps somehow
100	has_intensity
101			= false, use_gm
102			= version?0 > 6 || version?1 > 7;
103	has_channel
104			= false, use_gm
105			= version?0 > 6 || version?1 > 7;
106
107	system0 cmd = system_image0 cmd;
108	system cmd x = map_unary (system_image cmd) x;
109	system2 cmd x y = map_binary (system_image2 cmd) x y;
110	system3 cmd x y z = map_trinary (system_image3 cmd) x y z;
111
112	radius_widget = Scale "Radius" 0 100 10;
113	sigma_widget = Scale "Sigma" 0.1 10 1;
114	angle_widget = Scale "Angle (degrees)" (-360) 360 0;
115	text_widget = String "Text to draw" "AaBbCcDdEe";
116
117	gamma_widget = Scale "Gamma" 0 10 1;
118	colors_widget = Scale "Colors" 1 10 3;
119	resize_widget = Scale "Resize (percent)" 0 500 100;
120	fuzz_widget = Scale "Fuzz (percent)" 0 100 0;
121	blur_rad_widget = Scale "Radius (0=auto)" 0 100 0;
122
123	// a colour with no enclosing quotes ... use this if we know there are
124	// some quotes at an outer level
125	print_colour_nq triple
126		= concat ["#", concat (map fmt triple)]
127	{
128		fmt x = reverse (take 2 (reverse (print_base 16 (x + 256))));
129	}
130
131	// we need the quotes because # is the comment character in *nix
132	print_colour triple = "\"" ++ print_colour_nq triple ++ "\"";
133
134	Foreground triple = class
135		Colour "sRGB" triple {
136
137		_flag = "-fill " ++ print_colour triple;
138
139		Colour_edit space triple = this.Foreground triple;
140	}
141	foreground_widget = Foreground [0, 0, 0];
142
143	GeneralCol triple = class
144		Colour "sRGB" triple {
145
146		_flag = print_colour_nq triple;
147
148		Colour_edit space triple = this.GeneralCol triple;
149	}
150	generalcol_widget = GeneralCol [0, 0, 0];
151
152	Background triple = class
153		Colour "sRGB" triple {
154
155		isNone = Toggle "None (transparent black)" false;
156
157		_flag = "-background " ++ if isNone then "None" else print_colour triple;
158
159		Colour_edit space triple = this.Background triple;
160	}
161	background_widget = Background [255, 255, 255];
162
163	Bordercol triple = class
164		Colour "sRGB" triple {
165
166		_flag = "-bordercolor " ++ print_colour triple;
167
168		Colour_edit space triple = this.Bordercol triple;
169	}
170	bordercol_widget = Bordercol [0, 0, 0];
171
172	Mattecol triple = class
173		Colour "sRGB" triple {
174
175		_flag = "-mattecolor " ++ print_colour triple;
176
177		Colour_edit space triple = this.Mattecol triple;
178	}
179	mattecol_widget = Mattecol [189, 189, 189];
180
181	// FIXME: Undercolour, like many others, can have alpha channel.
182	// How does user input this? With a slider?
183	Undercol triple = class
184		Colour "sRGB" triple {
185
186		isNone = Toggle "None (transparent black)" true;
187
188		_flag = if isNone then "" else ("-undercolor " ++ print_colour triple);
189
190		Colour_edit space triple = this.Undercol triple;
191	}
192	undercol_widget = Undercol [0, 0, 0];
193
194	changeCol_widget = class {
195		_vislevel = 3;
196
197		colour = GeneralCol [0, 0, 0];
198		fuzz = fuzz_widget;
199		nonMatch = Toggle "change non-matching colours" false;
200	}
201
202	Alpha alpha = class
203		Option_string "Alpha" [
204			"On",
205			"Off",
206			"Set",
207			"Opaque",
208			"Transparent",
209			"Extract",
210			"Copy",
211			"Shape",
212			"Remove",
213			"Background"
214		] alpha {
215
216		_flag = "-alpha " ++ alpha;
217
218		Option_edit caption labels value = this.Alpha labels?value;
219	}
220	alpha_widget = Alpha "On";
221
222	Antialias value = class
223		Toggle "Antialias" value {
224
225		_flag
226			= "-antialias", value
227			= "+antialias";
228
229		Toggle_edit caption value = this.Antialias value;
230	}
231	antialias_widget = Antialias true;
232
233	Builtin builtin = class
234		Option_string "Builtin" [
235			// See http://www.imagemagick.org/script/formats.php
236			"rose:",
237			"logo:",
238			"wizard:",
239			"granite:",
240			"netscape:"
241		] builtin {
242
243		_flag = builtin;
244
245		Option_edit caption labels value = this.Builtin labels?value;
246	}
247	builtin_widget = Builtin "rose:";
248
249
250	channels_widget = class {
251		// FIXME? Can we grey-out alpha when we have no alpha channel,
252		//        show CMY(K) instead of RGB(K) etc?
253		// Yes, perhaps we can create different widgets for RGB, RGBA, CMY, CMYK, CMYA, CMYKA.
254		ChanR valueR = class
255			Toggle "Red" valueR {
256
257			_flag
258				= "R", valueR
259				= "";
260
261			Toggle_edit caption valueR = this.ChanR valueR;
262		}
263		channelR = ChanR true;
264
265		ChanG valueG = class
266			Toggle "Green" valueG {
267
268			_flag
269				= "G", valueG
270				= "";
271
272			Toggle_edit caption valueG = this.ChanG valueG;
273		}
274		channelG = ChanG true;
275
276		ChanB valueB = class
277			Toggle "Blue" valueB {
278
279			_flag
280				= "B", valueB
281				= "";
282
283			Toggle_edit caption valueB = this.ChanB valueB;
284		}
285		channelB = ChanB true;
286
287		ChanK valueK = class
288			Toggle "Black" valueK {
289
290			_flag
291				= "K", valueK
292				= "";
293
294			Toggle_edit caption valueK = this.ChanK valueK;
295		}
296		channelK = ChanK true;
297
298		ChanA valueA = class
299			Toggle "Alpha" valueA {
300
301			_flag
302				= "A", valueA
303				= "";
304
305			Toggle_edit caption valueA = this.ChanA valueA;
306		}
307		channelA = ChanA false;
308
309		ChanSy valueSy = class
310			Toggle "Sync" valueSy {
311
312			_flag
313				= ",sync", valueSy
314				= "";
315
316			Toggle_edit caption valueSy = this.ChanSy valueSy;
317		}
318		channelSy = ChanSy true;
319
320		_rgbka = concat [channelR._flag,
321				channelG._flag,
322				channelB._flag,
323				channelK._flag,
324				channelA._flag
325			];
326
327		_flag
328			= "", _rgbka == "" || !has_channel
329			= concat [ "-channel ",
330				_rgbka,
331				channelSy._flag
332				];
333	}
334
335	ch_widget = channels_widget;
336
337	Colorspace colsp = class
338		Option_string "Colorspace" [
339			"CIELab",
340			"CMY",
341			"CMYK",
342			"Gray",
343			"HCL",
344			"HCLp",
345			"HSB",
346			"HSI",
347			"HSL",
348			"HSV",
349			"HWB",
350			"Lab",
351			"LCH",
352			"LCHab",
353			"LCHuv",
354			"LMS",
355			"Log",
356			"Luv",
357			"OHTA",
358			"Rec601Luma",
359			"Rec601YCbCr",
360			"Rec709Luma",
361			"Rec709YCbCr",
362			"RGB",
363			"scRGB",
364			"sRGB",
365			"Transparent",
366			"XYZ",
367			"YCbCr",
368			"YDbDr",
369			"YCC",
370			"YIQ",
371			"YPbPr",
372			"YUV"
373		] colsp {
374
375		_flag = colsp;
376
377		Option_edit caption labels value = this.Colorspace labels?value;
378	}
379	colorspace_widget = Colorspace "sRGB";
380
381	Compose comp = class
382		Option_string "Compose method" [
383			"Atop",
384			"Blend",
385			"Blur",
386			"Bumpmap",
387			"ChangeMask",
388			"Clear",
389			"ColorBurn",
390			"ColorDodge",
391			"Colorize",
392			"CopyBlack",
393			"CopyBlue",
394			"CopyCyan",
395			"CopyGreen",
396			"Copy",
397			"CopyMagenta",
398			"CopyOpacity",
399			"CopyRed",
400			"CopyYellow",
401			"Darken",
402			"DarkenIntensity",
403			"DivideDst",
404			"DivideSrc",
405			"Dst",
406			"Difference",
407			"Displace",
408			"Dissolve",
409			"Distort",
410			"DstAtop",
411			"DstIn",
412			"DstOut",
413			"DstOver",
414			"Exclusion",
415			"HardLight",
416			"Hue",
417			"In",
418			"Lighten",
419			"LightenIntensity",
420			"LinearBurn",
421			"LinearDodge",
422			"LinearLight",
423			"Luminize",
424			"Mathematics",
425			"MinusDst",
426			"MinusSrc",
427			"Modulate",
428			"ModulusAdd",
429			"ModulusSubtract",
430			"Multiply",
431			"None",
432			"Out",
433			"Overlay",
434			"Over",
435			"PegtopLight",
436			"PinLight",
437			"Plus",
438			"Replace",
439			"Saturate",
440			"Screen",
441			"SoftLight",
442			"Src",
443			"SrcAtop",
444			"SrcIn",
445			"SrcOut",
446			"SrcOver",
447			"VividLight",
448			"Xor"
449		] comp {
450
451		_flag = "-compose " ++ comp;
452
453		Option_edit caption labels value = this.Compose labels?value;
454	}
455	compose_widget = Compose "Over";
456	// FIXME: Some compose mehods (Displace, Distort, Mathematics) need a string.
457
458	// FIXME: we could use a class that does both -compose and -intensity, for methods LightenIntensity, DarkenIntensity, CopyOpacity, CopyBlack
459
460	coordinate_widget = class {
461		_vislevel = 3;
462
463		x = Expression "X" 0;
464		y = Expression "Y" 0;
465
466		_flag = concat [print x.expr, ",", print y.expr];
467	};
468
469	Distort distort = class
470		Option_string "Distort" [
471			"Affine",
472			"AffineProjection",
473			"ScaleRotateTranslate",
474			"SRT",
475			"Perspective",
476			"PerspectiveProjection",
477			"BilinearForward",
478			"BilinearReverse",
479			"Polynomial",
480			"Arc",
481			"Polar",
482			"DePolar",
483			"Barrel",
484			"BarrelInverse",
485			"Shepards",
486			"Resize"
487		] distort {
488
489		_flag = distort;
490
491		Option_edit caption labels value = this.Distort labels?value;
492	}
493	distort_widget = Distort "SRT";
494
495	Dither dither = class
496		Option_string "Dither" [
497			"None",
498			"FloydSteinberg",
499			"Riemersma"
500		] dither {
501
502		_flag = "-dither " ++ dither;
503
504		Option_edit caption labels value = this.Dither labels?value;
505	}
506	dither_widget = Dither "FloydSteinberg";
507
508	Evaluate eval = class
509		Option_string "Evaluate operation" [
510			"Abs",
511			"Add",
512			"AddModulus",
513			"And",
514			"Cos",
515			"Cosine",
516			"Divide",
517			"Exp",
518			"Exponential",
519			"GaussianNoise",
520			"ImpulseNoise",
521			"LaplacianNoise",
522			"LeftShift",
523			"Log",
524			"Max",
525			"Mean",
526			"Median",
527			"Min",
528			"MultiplicativeNoise",
529			"Multiply",
530			"Or",
531			"PoissonNoise",
532			"Pow",
533			"RightShift",
534			"Set",
535			"Sin",
536			"Sine",
537			"Subtract",
538			"Sum",
539			"Threshold",
540			"ThresholdBlack",
541			"ThresholdWhite",
542			"UniformNoise",
543			"Xor"
544		] eval {
545
546		_flag = "-evaluate " ++ eval;
547
548		Option_edit caption labels value = this.Evaluate labels?value;
549	}
550	evaluate_widget = Evaluate "Add";
551
552	Filter filt = class
553		Option_string "Filter" [
554			"default",
555			"Bartlett",
556			"Blackman",
557			"Bohman",
558			"Box",
559			"Catrom",
560			"Cosine",
561			"Cubic",
562			"Gaussian",
563			"Hamming",
564			"Hann",
565			"Hermite",
566			"Jinc",
567			"Kaiser",
568			"Lagrange",
569			"Lanczos",
570			"Lanczos2",
571			"Lanczos2Sharp",
572			"LanczosRadius",
573			"LanczosSharp",
574			"Mitchell",
575			"Parzen",
576			"Point",
577			"Quadratic",
578			"Robidoux",
579			"RobidouxSharp",
580			"Sinc",
581			"SincFast",
582			"Spline",
583			"Triangle",
584			"Welch"
585		] filt {
586
587		_flag = if filt == "default" then "" else "-filter " ++ filt;
588
589		Option_edit caption labels value = this.Filter labels?value;
590	}
591	filter_widget = Filter "default";
592
593	Function func = class
594		Option_string "Function" [
595			"Polynomial",
596			"Sinusoid",
597			"Arcsin",
598			"Arctan"
599		] func {
600
601		_flag = func;
602
603		Option_edit caption labels value = this.Function labels?value;
604	}
605	function_widget = Function "Polynomial";
606
607//  "Polynomial (a[n], a[n-1], ... a[1], a[0])",
608//  "Sinusoid (freq, phase, amp, bias)",
609//  "Arcsin (width, centre, range, bias)",
610//  "Arctan (slope, centre, range, bias)"
611
612	Gravity gravity = class
613		Option_string "Gravity" [
614			"None",
615			"Center",
616			"East",
617			"Forget",
618			"NorthEast",
619			"North",
620			"NorthWest",
621			"SouthEast",
622			"South",
623			"SouthWest",
624			"West",
625			"Static"
626		] gravity {
627
628		_flag = "-gravity " ++ gravity;
629
630		Option_edit caption labels value = this.Gravity labels?value;
631	}
632	gravity_widget = Gravity "Center";
633
634	ImageType imagetype = class
635		Option_string "Image type" [
636			"Bilevel",
637			"ColorSeparation",
638			"ColorSeparationAlpha",
639			"ColorSeparationMatte",
640			"Grayscale",
641			"GrayscaleAlpha",
642			"GrayscaleMatte",
643			"Optimize",
644			"Palette",
645			"PaletteBilevelAlpha",
646			"PaletteBilevelMatte",
647			"PaletteAlpha",
648			"PaletteMatte",
649			"TrueColorAlpha",
650			"TrueColorMatte",
651			"TrueColor"
652		] imagetype {
653
654		_flag = "-type " ++ imagetype;
655
656		Option_edit caption labels value = this.ImageType labels?value;
657	}
658	imagetype_widget = ImageType "TrueColor";
659
660	Intensity intensity = class
661		Option_string "Intensity (gray conversion)" [
662			"Average",
663			"Brightness",
664			"Lightness",
665			"MS",
666			"Rec601Luma",
667			"Rec601Luminance",
668			"Rec709Luma",
669			"Rec709Luminance",
670			"RMS"
671		] intensity {
672
673		_flag
674			= "-intensity " ++ intensity, has_intensity
675			= "";
676
677		Option_edit caption labels value = this.Intensity labels?value;
678	}
679	intensity_widget = Intensity "Rec709Luminance";
680
681	Interpolate interp = class
682		Option_string "Interpolate" [
683			"default",
684			"Average",
685			"Average4",
686			"Average9",
687			"Average16",
688			"Background",
689			"Bilinear",
690			"Blend",
691			"Integer",
692			"Mesh",
693			"Nearest",
694			"NearestNeighbor",
695			"Spline"
696		] interp {
697
698		_flag = if interp == "default" then "" else "-interpolate " ++ interp;
699
700		Option_edit caption labels value = this.Interpolate labels?value;
701	}
702	interpolate_widget = Interpolate "default";
703
704	Kernel kernel = class
705		Option_string "Kernel" [
706			"Unity",
707			"Gaussian",
708			"DoG",
709			"LoG",
710			"Blur",
711			"Comet",
712			"Binomial",
713			"Laplacian",
714			"Sobel",
715			"FreiChen",
716			"Roberts",
717			"Prewitt",
718			"Compass",
719			"Kirsch",
720			"Diamond",
721			"Square",
722			"Rectangle",
723			"Disk",
724			"Octagon",
725			"Plus",
726			"Cross",
727			"Ring",
728			"Peaks",
729			"Edges",
730			"Corners",
731			"Diagonals",
732			"LineEnds",
733			"LineJunctions",
734			"Ridges",
735			"ConvexHull",
736			"ThinSe",
737			"Skeleton",
738			"Chebyshev",
739			"Manhattan",
740			"Octagonal",
741			"Euclidean"
742			// FIXME: custom kernel
743		] kernel {
744
745		_flag = kernel;
746
747		Option_edit caption labels value = this.Kernel labels?value;
748	}
749	kernel_widget = Kernel "Unity";
750
751	ModColSp msp = class
752		Option_string "modulate colorspace" [
753			"HCL",
754			"HCLp",
755			"HSB",
756			"HSI",
757			"HSL",
758			"HSV",
759			"HWB",
760			"LCH"
761		] msp {
762
763		_flag = "-set option:modulate:colorspace " ++ msp;
764
765		Option_edit caption labels value = this.ModColSp labels?value;
766	}
767	ModColSp_widget = ModColSp "HSL";
768
769	MorphMeth morph = class
770		Option_string "Method" [
771			"Correlate",
772			"Convolve",
773			"Dilate",
774			"Erode",
775			"Close",
776			"Open",
777			"DilateIntensity",
778			"ErodeIntensity",
779			"CloseIntensity",
780			"OpenIntensity",
781			"Smooth",
782			"EdgeOut",
783			"EdgeIn",
784			"Edge",
785			"TopHat",
786			"BottomHat",
787			"HitAndMiss",
788			"Thinning",
789			"Thicken",
790			"Distance",
791			"IterativeDistance"
792		] morph {
793
794		_flag = morph;
795
796		Option_edit caption labels value = this.MorphMeth labels?value;
797	}
798	morphmeth_widget = MorphMeth "Dilate";
799
800	Noise noise = class
801		Option_string "Noise" [
802			"Gaussian",
803			"Impulse",
804			"Laplacian",
805			"Multiplicative",
806			"Poisson",
807			"Random",
808			"Uniform"
809		] noise {
810
811		_flag = "+noise " ++ noise;
812
813		Option_edit caption labels value = this.Noise labels?value;
814	}
815	noise_widget = Noise "Gaussian";
816
817	Pattern pattern = class
818		Option_string "Noise" [
819			// See http://www.imagemagick.org/script/formats.php
820			"bricks",
821			"checkerboard",
822			"circles",
823			"crosshatch",
824			"crosshatch30",
825			"crosshatch45",
826			"gray0",
827			"gray5",
828			"gray10",
829			"gray15",
830			"gray20",
831			"gray25",
832			"gray30",
833			"gray35",
834			"gray40",
835			"gray45",
836			"gray50",
837			"gray55",
838			"gray60",
839			"gray65",
840			"gray70",
841			"gray75",
842			"gray80",
843			"gray85",
844			"gray90",
845			"gray95",
846			"gray100",
847			"hexagons",
848			"horizontal",
849			"horizontal2",
850			"horizontal3",
851			"horizontalsaw",
852			"hs_bdiagonal",
853			"hs_cross",
854			"hs_diagcross",
855			"hs_fdiagonal",
856			"hs_horizontal",
857			"hs_vertical",
858			"left30",
859			"left45",
860			"leftshingle",
861			"octagons",
862			"right30",
863			"right45",
864			"rightshingle",
865			"smallfishscales",
866			"vertical",
867			"vertical2",
868			"vertical3",
869			"verticalbricks",
870			"verticalleftshingle",
871			"verticalrightshingle",
872			"verticalsaw"
873		] pattern {
874
875		_flag = "pattern:" ++ pattern;
876
877		Option_edit caption labels value = this.Pattern labels?value;
878	}
879	pattern_widget = Pattern "bricks";
880
881	ResizeType resizet = class
882		Option_string "Resize type" [
883			"resize",
884			"scale",
885			"sample",
886			"adaptive-resize"
887		] resizet {
888
889		_flag = resizet;
890
891		Option_edit caption labels value = this.ResizeType labels?value;
892	}
893	ResizeType_widget = ResizeType "resize";
894
895	Size_widget = class {
896		_vislevel = 3;
897
898		width = Expression "Width (pixels)" 64;
899		height = Expression "Height (pixels)" 64;
900
901		_flag = "-size " ++
902			print width.expr ++ "x" ++ print height.expr;
903
904	};
905
906	StatType statt = class
907		Option_string "Statistic type" [
908			"Gradient",
909			"Maximum",
910			"Mean",
911			"Median",
912			"Minimum",
913			"Mode",
914			"Nonpeak",
915			"StandardDeviation"
916		] statt {
917
918		_flag = statt;
919
920		Option_edit caption labels value = this.StatType labels?value;
921	}
922	StatType_widget = StatType "Mean";
923
924	VirtualPixel vp = class
925		Option_string "Virtual pixel" [
926			"Background",
927			"Black",
928			"CheckerTile",
929			"Dither",
930			"Edge",
931			"Gray",
932			"HorizontalTile",
933			"HorizontalTileEdge",
934			"Mirror",
935			"None",
936			"Random",
937			"Tile",
938			"Transparent",
939			"VerticalTile",
940			"VerticalTileEdge",
941			"White"
942		] vp {
943
944		_flag = "-virtual-pixel " ++ vp;
945
946		_isBackground = (vp == "Background");
947
948		Option_edit caption labels value = this.VirtualPixel labels?value;
949	}
950	VirtualPixel_widget = VirtualPixel "Edge";
951
952	VirtualPixelBack_widget = class {
953		virtpix = Magick.VirtualPixel_widget;
954		background = Magick.background_widget;
955		_flag = (if virtpix._isBackground then (background._flag ++ " ") else "")
956			++ virtpix._flag;
957	}
958
959	Geometry_widget = class {
960		_vislevel = 3;
961
962		x = Expression "X" 0;
963		y = Expression "Y" 0;
964		hoffset = Expression "Horizontal offset" 0;
965		voffset = Expression "Vertical offset" 0;
966
967		_flag
968			= concat [print x.expr, "x", print y.expr,
969				format hoffset, format voffset]
970		{
971			// print an offset ... we want '+' in front of +ve strings
972			format offset
973				= concat ["+", print offset.expr], offset.expr >= 0
974				= print offset.expr;
975		}
976	};
977
978	AnnotGeometry_widget = class {
979		_vislevel = 3;
980
981		shearX = Expression "shear X (degrees)" 0;
982		shearY = Expression "shear Y (degrees)" 0;
983		hoffset = Expression "Horizontal offset" 0;
984		voffset = Expression "Vertical offset" 0;
985
986		_flag
987			= concat [print shearX.expr, "x", print shearY.expr,
988				format hoffset, format voffset]
989		{
990			// print an offset ... we want '+' in front of +ve strings
991			format offset
992				= concat ["+", print offset.expr], offset.expr >= 0
993				= print offset.expr;
994		}
995	};
996
997	OffsetGeometry_widget = class {
998		_vislevel = 3;
999
1000		hoffset = Expression "Horizontal offset" 0;
1001		voffset = Expression "Vertical offset" 0;
1002
1003		_flag = concat [format hoffset, format voffset]
1004		{
1005			// print an offset ... we want '+' in front of +ve strings
1006			format offset
1007				= concat ["+", print offset.expr], offset.expr >= 0
1008				= print offset.expr;
1009		}
1010	};
1011
1012	WhxyGeometry_widget = class {
1013		_vislevel = 3;
1014
1015		x = Expression "Width" 0;
1016		y = Expression "Height" 0;
1017		hoffset = Expression "Horizontal offset" 0;
1018		voffset = Expression "Vertical offset" 0;
1019
1020		_flag
1021			= concat [print x.expr, "x", print y.expr,
1022				format hoffset, format voffset]
1023		{
1024			// print an offset ... we want '+' in front of +ve strings
1025			format offset
1026				= concat ["+", print offset.expr], offset.expr >= 0
1027				= print offset.expr;
1028		}
1029	};
1030
1031	FrameGeometry_widget = class {
1032		_vislevel = 3;
1033
1034		x = Expression "Width" 0;
1035		y = Expression "Height" 0;
1036		outbev = Expression "Outer bevel thickness" 0;
1037		inbev = Expression "Inner bevel thickness" 0;
1038
1039		_flag
1040			= concat [print x.expr, "x", print y.expr,
1041				format outbev, format inbev]
1042		{
1043			// print an offset ... we want '+' in front of +ve strings
1044			format offset
1045				= concat ["+", print offset.expr], offset.expr >= 0
1046				= print offset.expr;
1047		}
1048	};
1049
1050	Font_widget = class {
1051		_vislevel = 3;
1052
1053		family = Option_string "Family" [
1054			"Arial",
1055			"ArialBlack",
1056			"AvantGarde",
1057			"BitstreamCharter",
1058			"Bookman",
1059			"CenturySchoolbook",
1060			"ComicSansMS",
1061			"Courier",
1062			"CourierNew",
1063			"DejaVuSans",
1064			"DejaVuSansMono",
1065			"DejaVuSerif",
1066			"Dingbats",
1067			"FreeMono",
1068			"FreeSans",
1069			"FreeSerif",
1070			"Garuda",
1071			"Georgia",
1072			"Helvetica",
1073			"HelveticaNarrow",
1074			"Impact",
1075			"LiberationMono",
1076			"LiberationSans",
1077			"LiberationSerif",
1078			"NewCenturySchlbk",
1079			"Palatino",
1080			"Purisa",
1081			"Symbol",
1082			"Times",
1083			"TimesNewRoman",
1084			"Ubuntu",
1085			"Verdana",
1086			"Webdings"
1087		] "Arial";
1088		style = Option_string "Style" [
1089			"Any", "Italic", "Normal", "Oblique"
1090		] "Normal";
1091		weight = Scale "Weight" 1 800 400;
1092		size = Scale "Point size" 1 100 12;
1093		stretch = Option_string "Stretch" [
1094			"Any", "Condensed", "Expanded", "ExtraCondensed", "ExtraExpanded",
1095			"Normal", "SemiCondensed", "SemiExpanded", "UltraCondensed",
1096			"UltraExpanded"
1097		] "Normal";
1098
1099		_flag = join_sep " " [
1100				"-family", family.item,
1101				"-weight", print weight.value,
1102				"-pointsize", print size.value,
1103				"-style", style.item,
1104				"-stretch", stretch.item];
1105	}
1106}
1107
1108