File Coverage

blib/lib/Image/Magick/CommandParser.pm
Criterion Covered Total %
statement 55 151 36.4
branch 1 18 5.5
condition 0 3 0.0
subroutine 14 26 53.8
pod 11 15 73.3
total 81 213 38.0


line stmt bran cond sub pod time code
1             package Image::Magick::CommandParser;
2              
3 1     1   17384 use strict;
  1         3  
  1         32  
4 1     1   4 use warnings;
  1         1  
  1         27  
5 1     1   2 use warnings qw(FATAL utf8); # Fatalize encoding glitches.
  1         4  
  1         35  
6              
7 1     1   379 use Data::Section::Simple 'get_data_section';
  1         449  
  1         48  
8              
9 1     1   4 use File::Glob;
  1         3  
  1         61  
10 1     1   416 use File::Slurper 'read_lines';
  1         21183  
  1         92  
11              
12 1     1   1127 use Log::Handler;
  1         52165  
  1         6  
13              
14 1     1   631 use Moo;
  1         7077  
  1         5  
15              
16 1     1   1560 use Set::Array;
  1         7773  
  1         13  
17              
18 1     1   588 use Set::FA::Element;
  1         2937  
  1         9  
19              
20 1     1   576 use Types::Standard qw/Any ArrayRef HashRef Str/;
  1         54133  
  1         11  
21              
22             has built_in_images =>
23             (
24             default => sub{return ''},
25             is => 'rw',
26             isa => Str,
27             required => 0,
28             );
29              
30             has built_in_patterns =>
31             (
32             default => sub{return ''},
33             is => 'rw',
34             isa => Str,
35             required => 0,
36             );
37              
38             has command =>
39             (
40             default => sub{return ''},
41             is => 'rw',
42             isa => Str,
43             required => 0,
44             );
45              
46             has dfa =>
47             (
48             default => sub{return ''},
49             is => 'rw',
50             isa => Any,
51             required => 0,
52             );
53              
54             has field =>
55             (
56             default => sub{return []},
57             is => 'rw',
58             isa => ArrayRef,
59             required => 0,
60             );
61              
62             has image_formats =>
63             (
64             default => sub{return ''},
65             is => 'rw',
66             isa => Str,
67             required => 0,
68             );
69              
70             has logger =>
71             (
72             default => sub{return undef},
73             is => 'rw',
74             isa => Any,
75             required => 0,
76             );
77              
78             has maxlevel =>
79             (
80             default => sub{return 'notice'},
81             is => 'rw',
82             isa => Str,
83             required => 0,
84             );
85              
86             has minlevel =>
87             (
88             default => sub{return 'error'},
89             is => 'rw',
90             isa => Str,
91             required => 0,
92             );
93              
94             has pseudo_image_formats =>
95             (
96             default => sub{return ''},
97             is => 'rw',
98             isa => Str,
99             required => 0,
100             );
101              
102             has stack =>
103             (
104             default => sub{return []},
105             is => 'rw',
106             isa => Any,
107             required => 0,
108             );
109              
110             my($myself); # For use inside functions.
111              
112             our $VERSION = '1.04';
113              
114             # -----------------------------------------------
115              
116             sub BUILD
117             {
118 1     1 0 12 my($self) = @_;
119 1         2 $myself = $self;
120              
121 1 50       15 if (! defined $self -> logger)
122             {
123 1         17 $self -> logger(Log::Handler -> new);
124 1         105 $self -> logger -> add
125             (
126             screen =>
127             {
128             maxlevel => $self -> maxlevel,
129             message_layout => '%m',
130             minlevel => $self -> minlevel,
131             utf8 => 1,
132             }
133             );
134             }
135              
136             } # End of BUILD.
137              
138             # ----------------------------------------------
139             # Warning: this a function, not a method.
140              
141             sub action
142             {
143 0     0 1 0 my($dfa) = @_;
144 0         0 my($name) = 'action';
145 0         0 my($match) = $dfa -> match;
146              
147 0         0 $myself -> log(debug => "'$name' matched '$match'");
148              
149 0 0       0 if (substr($match, 0, 1) eq '@')
150             {
151 0         0 my($field) = $myself -> field;
152              
153 0         0 $myself -> field([join(' ', read_lines substr($match, 1) ), @$field]);
154             }
155             else
156             {
157 0         0 $myself -> stack -> push
158             ({
159             token => $match,
160             type => $name,
161             });
162             }
163              
164              
165             } # End of action.
166              
167             # ----------------------------------------------
168             # Warning: this a function, not a method.
169              
170             sub close_parenthesis
171             {
172 0     0 1 0 my($dfa) = @_;
173 0         0 my($name) = 'close_parenthesis';
174 0         0 my($match) = $dfa -> match;
175              
176 0         0 $myself -> log(debug => "'$name' matched '$match'");
177              
178 0         0 $myself -> stack -> push
179             ({
180             token => $match,
181             type => $name,
182             });
183              
184             } # End of close_parenthesis.
185              
186             # ----------------------------------------------
187             # Warning: this a function, not a method.
188              
189             sub done
190             {
191 0     0 1 0 my($dfa) = @_;
192 0         0 my($name) = 'done';
193 0         0 my($match) = $dfa -> match;
194              
195 0         0 $myself -> log(debug => "'$name' matched '$match'");
196              
197 0         0 $myself -> stack -> push
198             ({
199             token => $match,
200             type => $name,
201             });
202              
203             } # End of done.
204              
205             # ----------------------------------------------
206             # Warning: this a function, not a method.
207              
208             sub file_glob
209             {
210 0     0 0 0 my($dfa) = @_;
211 0         0 my($name) = 'input_file';
212 0         0 my($match) = $dfa -> match;
213              
214 0         0 $myself -> log(debug => "'$name' matched '$match'");
215              
216             # Warning! Do not use (sort bsd_glob($match) ), when bs_glob() returns the unglobbed 'colors/*s*.png'
217             # in test 47.
218             # You can use (sort map{$_} bsd_glob($match) ) but the result is ASCII sorted (by default) anyway.
219              
220 0         0 for my $file (File::Glob::bsd_glob($match) )
221             {
222 0         0 $myself -> stack -> push
223             ({
224             token => $file,
225             type => $name,
226             });
227             }
228              
229             } # End of file_glob.
230              
231             # ----------------------------------------------
232             # Warning: this a function, not a method.
233              
234             sub input_file
235             {
236 0     0 1 0 my($dfa) = @_;
237 0         0 my($name) = 'input_file';
238 0         0 my($match) = $dfa -> match;
239              
240 0         0 $myself -> log(debug => "'$name' matched '$match'");
241              
242 0         0 $myself -> stack -> push
243             ({
244             token => $match,
245             type => $name,
246             });
247              
248             } # End of input_file.
249              
250             # ----------------------------------------------
251             # Warning: this a function, not a method.
252              
253             sub kommand
254             {
255 0     0 0 0 my($dfa) = @_;
256 0         0 my($name) = 'command';
257 0         0 my($match) = $dfa -> match;
258              
259 0         0 $myself -> log(debug => "'$name' matched '$match'");
260              
261 0         0 $myself -> stack -> push
262             ({
263             token => $match,
264             type => $name,
265             });
266              
267             } # End of kommand.
268              
269             # --------------------------------------------------
270              
271             sub log
272             {
273 0     0 1 0 my($self, $level, $s) = @_;
274              
275 0 0       0 $self -> logger -> log($level => $s) if ($self -> logger);
276              
277             } # End of log.
278              
279             # --------------------------------------------------
280              
281             sub new_machine
282             {
283 1     1 0 1 my($self) = @_;
284 1         5 my($list) = get_data_section('built_in_images');
285              
286 1         717 $self -> built_in_images(join('|', split(/\n/, $list) ) );
287              
288 1         36 my($built_in_images) = $self -> built_in_images;
289              
290             # Warning: Do not sort these formats. Things like 'o' must come after all /o.+/.
291              
292 1         6 $list = get_data_section('image_formats');
293              
294 1         586 $self -> image_formats(join('|', split(/\n/, $list) ) );
295              
296 1         62 my($image_formats) = $self -> image_formats;
297              
298 1         6 $list = get_data_section('built_in_patterns');
299              
300 1         662 $self -> built_in_patterns(join('|', split(/\n/, $list) ) );
301              
302 1         38 my($built_in_patterns) = $self -> built_in_patterns;
303              
304 1         6 $list = get_data_section('pseudo_image_formats');
305              
306 1         561 $self -> pseudo_image_formats(join('|', split(/\n/, $list) ) );
307              
308 1         38 my($pseudo_image_formats) = $self -> pseudo_image_formats;
309 1         140 my($transitions) =
310             [
311             # Current state Regexp testing input New state
312             #
313             # Warning: If you patch 'action', copy the patch down to 'action_1'.
314              
315             ['action', '^$', 'done'],
316             ['action', '[-+][a-zA-Z]+', 'action_1'],
317             ['action', '\(', 'open_parenthesis'],
318             ['action', '\)', 'close_parenthesis'],
319             ['action', '[\"\'].*[\"\']', 'parameter'],
320             ['action', '\@.+', 'action_1'],
321             ['action', '\d+%x\d+%[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
322             ['action', 'x\d+%[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
323             ['action', '\d+%(?:x\d+)?[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
324             ['action', '\d+x\d+%?[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
325             ['action', '-?\d+.*', 'parameter'],
326             ['action', "magick:(?:$built_in_images)", 'output_file'],
327             ['action', "(?:$built_in_images):", 'output_file'],
328             ['action', ".+\\.(?:$image_formats)", 'output_file'],
329             ['action', "(?:$image_formats):-", 'output_file'],
330             ['action', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
331             ['action', '[a-zA-Z][-a-zA-Z]+', 'parameter'],
332              
333             # Warning: If you patch 'action_1', copy the patch up to 'action'.
334              
335             ['action_1', '^$', 'done'],
336             ['action_1', '[-+][a-zA-Z]+', 'action'],
337             ['action_1', '\(', 'open_parenthesis'],
338             ['action_1', '\)', 'close_parenthesis'],
339             ['action_1', '[\"\'].*[\"\']', 'parameter'],
340             ['action_1', '\@.+', 'action'],
341             ['action_1', '\d+%x\d+%[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
342             ['action_1', 'x\d+%[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
343             ['action_1', '\d+%(?:x\d+)?[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
344             ['action_1', '\d+x\d+%?[!<>^]?(?:[-+]\d+[-+]\d+)?', 'parameter'],
345             ['action_1', '-?\d+.*', 'parameter'],
346             ['action_1', "magick:(?:$built_in_images)", 'output_file'],
347             ['action_1', "(?:$built_in_images):", 'output_file'],
348             ['action_1', ".+\\.(?:$image_formats)", 'output_file'],
349             ['action_1', "(?:$image_formats):-", 'output_file'],
350             ['action_1', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
351             ['action_1', '[a-zA-Z][-a-zA-Z]+', 'parameter'],
352              
353             ['command', '^$', 'done'],
354             ['command', '.*(?:\\*|\\?)', 'file_glob'],
355             ['command', 'rgb:(?:.+)', 'input_file'],
356             ['command', "magick:(?:$built_in_images)", 'input_file'],
357             ['command', "(?:$built_in_images):", 'input_file'],
358             ['command', "pattern:(?:$built_in_patterns)", 'input_file'],
359             ['command', "(?:$pseudo_image_formats):(?:.*)", 'input_file'],
360             ['command', ".+\\.(?:$image_formats)", 'input_file'],
361             ['command', '^-$', 'input_file'],
362             ['command', "(?:$image_formats):-", 'input_file'],
363             ['command', "(?:$image_formats):fd:\\d+", 'input_file'],
364             ['command', 'fd:\\d+', 'input_file'],
365             ['command', '[-+][a-zA-Z]+', 'action'],
366              
367             ['done', '^$', 'done'],
368              
369             ['file_glob', '^$', 'done'],
370             ['file_glob', '\(', 'open_parenthesis'],
371             ['file_glob', '.*(?:\\*|\\?)', 'file_glob'],
372             ['file_glob', 'rgb:(?:.+)', 'input_file_1'],
373             ['file_glob', "magick:(?:$built_in_images)", 'input_file_1'],
374             ['file_glob', "(?:$built_in_images):", 'input_file_1'],
375             ['file_glob', "pattern:(?:$built_in_patterns)", 'input_file_1'],
376             ['file_glob', "(?:$pseudo_image_formats):(?:.*)", 'input_file_1'],
377             ['file_glob', ".+\\.(?:$image_formats)", 'input_file_1'],
378             ['file_glob', '^-$', 'input_file_1'],
379             ['file_glob', "(?:$image_formats):-", 'input_file_1'],
380             ['file_glob', "(?:$image_formats):fd:\\d+", 'input_file_1'],
381             ['file_glob', 'fd:\\d+', 'input_file_1'],
382             ['file_glob', '[-+][a-zA-Z]+', 'action'],
383             ['file_glob', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
384              
385             # Warning: If you patch 'input_file', copy the patch down to 'input_file_1'.
386              
387             ['input_file', '^$', 'done'],
388             ['input_file', '.*(?:\\*|\\?)', 'file_glob'],
389             ['input_file', '\(', 'open_parenthesis'],
390             ['input_file', 'rgb:(?:.+)', 'input_file_1'],
391             ['input_file', "magick:(?:$built_in_images)", 'input_file_1'],
392             ['input_file', "(?:$built_in_images):", 'input_file_1'],
393             ['input_file', "pattern:(?:$built_in_patterns)", 'input_file_1'],
394             ['input_file', "(?:$pseudo_image_formats):(?:.*)", 'input_file_1'],
395             ['input_file', ".+\\.(?:$image_formats)", 'input_file_1'],
396             ['input_file', '^-$', 'input_file_1'],
397             ['input_file', "(?:$image_formats):-", 'input_file_1'],
398             ['input_file', "(?:$image_formats):fd:\\d+", 'input_file_1'],
399             ['input_file', 'fd:\\d+', 'input_file_1'],
400             ['input_file', '[-+][a-zA-Z]+', 'action'],
401             ['input_file', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
402              
403             # Warning: If you patch 'input_file_1', copy the patch up to 'input_file'.
404              
405             ['input_file_1', '^$', 'done'],
406             ['input_file_1', '.*(?:\\*|\\?)', 'file_glob'],
407             ['input_file_1', '\(', 'open_parenthesis'],
408             ['input_file_1', 'rgb:(?:.+)', 'input_file'],
409             ['input_file_1', "magick:(?:$built_in_images)", 'input_file'],
410             ['input_file_1', "(?:$built_in_images):", 'input_file'],
411             ['input_file_1', "pattern:(?:$built_in_patterns)", 'input_file'],
412             ['input_file_1', "(?:$pseudo_image_formats):(?:.*)", 'input_file'],
413             ['input_file_1', ".+\\.(?:$image_formats)", 'input_file'],
414             ['input_file_1', '^-$', 'input_file'],
415             ['input_file_1', "(?:$image_formats):-", 'input_file'],
416             ['input_file_1', "(?:$image_formats):fd:\\d+", 'input_file'],
417             ['input_file_1', 'fd:\\d+', 'input_file'],
418             ['input_file_1', '[-+][a-zA-Z]+', 'action'],
419             ['input_file_1', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
420              
421             ['close_parenthesis', '^$', 'done'],
422             ['close_parenthesis', '\(', 'open_parenthesis'],
423             ['close_parenthesis', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
424             ['close_parenthesis', '[-+][a-zA-Z]+', 'action'],
425             ['close_parenthesis', ".+\\.(?:$image_formats)", 'output_file'],
426             ['close_parenthesis', "(?:$image_formats):-", 'output_file'],
427              
428             ['open_parenthesis', '^$', 'done'],
429             ['open_parenthesis', '\)', 'close_parenthesis'],
430             ['open_parenthesis', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
431             ['open_parenthesis', '[-+][a-zA-Z]+', 'action'],
432             ['open_parenthesis', ".+\\.(?:$image_formats)", 'output_file'],
433             ['open_parenthesis', "(?:$image_formats):-", 'output_file'],
434              
435             ['operator', '^$', 'done'],
436             ['operator', '[-+][a-zA-Z]+', 'action'],
437             ['operator', ".+\\.(?:$image_formats)", 'output_file'],
438             ['operator', "(?:$image_formats):-", 'output_file'],
439              
440             ['output_file', '^$', 'done'],
441              
442             ['parameter', '^$', 'done'],
443             ['parameter', '\(', 'open_parenthesis'],
444             ['parameter', '\)', 'close_parenthesis'],
445             ['parameter', '[-+][a-zA-Z]+', 'action'],
446             ['parameter', '[a-zA-Z][-a-zA-Z]+:[a-zA-Z]+', 'operator'],
447             ['parameter', ".+\\.(?:$image_formats)", 'output_file'],
448             ['parameter', "(?:$image_formats):-", 'output_file'],
449              
450             ['start', '(?:convert|mogrify)', 'command'],
451             ];
452              
453             # Crank up the DFA.
454              
455 1         43 $self -> dfa
456             (
457             Set::FA::Element -> new
458             (
459             accepting => ['done'],
460             actions =>
461             {
462             action =>
463             {
464             entry => \&action,
465             },
466             action_1 =>
467             {
468             entry => \&action,
469             },
470             close_parenthesis =>
471             {
472             entry => \&close_parenthesis,
473             },
474             command =>
475             {
476             entry => \&kommand,
477             },
478             done =>
479             {
480             entry => \&done,
481             },
482             file_glob =>
483             {
484             entry => \&file_glob,
485             },
486             input_file =>
487             {
488             entry => \&input_file,
489             },
490             input_file_1 =>
491             {
492             entry => \&input_file,
493             },
494             open_parenthesis =>
495             {
496             entry => \&open_parenthesis,
497             },
498             operator =>
499             {
500             entry => \&operator,
501             },
502             output_file =>
503             {
504             entry => \&output_file,
505             },
506             parameter =>
507             {
508             entry => \&parameter,
509             },
510             },
511             die_on_loop => 1,
512             maxlevel => $self -> maxlevel,
513             start => 'start',
514             transitions => $transitions,
515             )
516             );
517              
518             } # End of new_machine.
519              
520             # ----------------------------------------------
521             # Warning: this a function, not a method.
522              
523             sub open_parenthesis
524             {
525 0     0 1 0 my($dfa) = @_;
526 0         0 my($name) = 'open_parenthesis';
527 0         0 my($match) = $dfa -> match;
528              
529 0         0 $myself -> log(debug => "'$name' matched '$match'");
530              
531 0         0 $myself -> stack -> push
532             ({
533             token => $match,
534             type => $name,
535             });
536              
537             } # End of open_parenthesis.
538              
539             # ----------------------------------------------
540             # Warning: this a function, not a method.
541              
542             sub output_file
543             {
544 0     0 1 0 my($dfa) = @_;
545 0         0 my($name) = 'output_file';
546 0         0 my($match) = $dfa -> match;
547              
548 0         0 $myself -> log(debug => "'$name' matched '$match'");
549              
550 0         0 $myself -> stack -> push
551             ({
552             token => $match,
553             type => $name,
554             });
555              
556             } # End of output_file.
557              
558             # ----------------------------------------------
559             # Warning: this a function, not a method.
560              
561             sub operator
562             {
563 0     0 1 0 my($dfa) = @_;
564 0         0 my($name) = 'operator';
565 0         0 my($match) = $dfa -> match;
566              
567 0         0 $myself -> log(debug => "'$name' matched '$match'");
568              
569 0         0 $myself -> stack -> push
570             ({
571             token => $match,
572             type => $name,
573             });
574              
575             } # End of operator.
576              
577             # ----------------------------------------------
578             # Warning: this a function, not a method.
579              
580             sub parameter
581             {
582 0     0 1 0 my($dfa) = @_;
583 0         0 my($name) = 'parameter';
584 0         0 my($match) = $dfa -> match;
585              
586 0         0 $myself -> log(debug => "'$name' matched '$match'");
587              
588 0         0 $myself -> stack -> push
589             ({
590             token => $match,
591             type => $name,
592             });
593              
594             } # End of parameter.
595              
596             # ------------------------------------------------
597              
598             sub result
599             {
600 0     0 1 0 my($self) = @_;
601              
602 0         0 return join(' ', map{$$_{token} } $self -> stack -> print);
  0         0  
603              
604             } # End of result.
605              
606             # ------------------------------------------------
607              
608             sub run
609             {
610 1     1 1 1755 my($self, %options) = @_;
611              
612             # The reason for resetting these each time is so the object can be reused.
613              
614 1         4 $self -> new_machine;
615 0           $self -> stack(Set::Array -> new);
616              
617             # Strip off any output file name.
618              
619 0 0         my($command) = $options{command} ? $options{command} : $self -> command;
620 0           $command =~ s/^\s+//;
621 0           $command =~ s/\s+$//;
622 0           my($output_file_name) = '';
623 0           my($image_regexp) = '^.+\s+(.+?\.(?:' . join('|', split/\n/, get_data_section('image_formats') ) . '))$';
624 0           $image_regexp = qr/$image_regexp/;
625              
626 0 0         if ($command =~ $image_regexp)
627             {
628 0           $output_file_name = $1;
629 0           $command = substr($command, 0, - length($output_file_name) - 1);
630              
631 0           $self -> log(debug => "Output file: $output_file_name");
632             }
633              
634 0           $self -> command($command);
635              
636 0           my(@field) = split(/\s+/, $command);
637 0           my($limit) = $#field;
638              
639             # Reconstruct strings like 'a b' which have been split just above.
640             # This code does not handle escaped spaces.
641              
642 0           my($quote);
643              
644 0           for (my $j = 0; $j < $limit; $j++)
645             {
646 0 0         next if (substr($field[$j], 0, 1) !~ /([\"\'])/); # The \ are for UltraEdit's syntax hiliter.
647              
648 0           $quote = $1;
649 0           my($k) = $j;
650              
651 0   0       while ( (++$k <= $limit) && ($field[$k] !~ /$quote$/) ) {};
652              
653 0 0         if ($k <= $limit)
654             {
655 0           splice(@field, $j, $k - $j + 1, join(' ', @field[$j .. $k]) );
656              
657 0           $limit -= $k - $j;
658             }
659             }
660              
661             # Here we jam @field into an attribute of the object because input like @include.me
662             # means the contents of that file have to be interpolated into the input stream.
663             # The interpolation takes place in function (not method) parameter().
664             # And we use $finished to allow for stand-alone 0 to be a field.
665              
666 0           $self -> field([@field]);
667              
668 0           my($finished) = 0;
669              
670 0           my($field);
671              
672 0           while (! $finished)
673             {
674 0           $field = shift @{$self -> field};
  0            
675              
676 0 0         if (! defined $field)
677             {
678 0           $finished = 1;
679             }
680             else
681             {
682 0           $self -> dfa -> step($field);
683             }
684             }
685              
686 0           $self -> log(info => '# At end, current state: ' . $self -> dfa -> current);
687 0           $self -> log(info => "# Processed input string: $command");
688              
689 0 0         if (length($output_file_name) > 0)
690             {
691 0           $myself -> stack -> push
692             ({
693             token => $output_file_name,
694             type => 'output_file_name',
695             });
696             }
697              
698             # Return 0 for success and 1 for failure.
699              
700 0           return 0;
701              
702             } # End of run.
703              
704             # ------------------------------------------------
705              
706             1;
707              
708             =pod
709              
710             =head1 NAME
711              
712             C<Image::Magick::CommandParser> - Parse any command line acceptable to convert or mogrify
713              
714             =head1 Synopsis
715              
716             This is scripts/synopsis.pl:
717              
718             #!/usr/bin/env perl
719              
720             use strict;
721             use warnings;
722             use warnings qw(FATAL utf8);
723              
724             use Image::Magick::CommandParser;
725              
726             # ----------------------------------------------
727              
728             my($command) = 'convert colors/*s*.png -append output.png';
729             my($processor) = Image::Magick::CommandParser -> new
730             (
731             command => $command,
732             maxlevel => 'notice',
733             );
734              
735             $processor -> run;
736              
737             print 'Input: ', $command, "\n";
738             print 'Result: ', $processor -> result, "\n";
739              
740             With its output (after running in the distro dir, with access to colors/*.png):
741              
742             Input: convert colors/*s*.png -append output.png
743             Result: convert colors/fuchsia.png colors/silver.png -append output.png
744              
745             =head1 Description
746              
747             C<Image::Magick::CommandParser> is a stand-alone parser for command lines acceptable to the
748             L<Imagemagick|https://imagemagick.org> programs C<convert> and C<mogrify>.
749              
750             It aims to handle all constructs supported by Imagemagick itself, but it's vital to understand
751             that this module does not use any component of Imagemagick. Hence the I<stand-alone> just above.
752              
753             In particular the output is a stack, accessible via the C<< $object -> stack >> method, which
754             returns an array of hashrefs.
755              
756             The stack is managed by an object of type L<Set::Array>. See the L</FAQ> for details.
757              
758             The result - as a space-separated string of tokens detected in the command - is returned by
759             L</result()>.
760              
761             The actual parsing is done with L<Set::FA::Element>.
762              
763             Consult the L</FAQ> and t/test.t for specific examples of command line options supported. A few of
764             them are included here:
765              
766             =over 4
767              
768             =item o All command options of the form [-+][a-zA-Z]+
769              
770             =item o Redirecting input from files
771              
772             =over 4
773              
774             =item o convert magick:rose -label @t/label.1.txt -format "%l label" rose.png
775              
776             =back
777              
778             =item o File globbing
779              
780             =over 4
781              
782             =item o convert colors/*s*.png -append output.png
783              
784             =back
785              
786             =item o Explicit image format
787              
788             =over 4
789              
790             =item o convert rgb:camera.image -size 320x85 output.png
791              
792             =back
793              
794             =item o Built-in images and patterns
795              
796             =over 4
797              
798             =item o convert pattern:bricks -size 320x85 output.png
799              
800             =back
801              
802             =item o Standard input and output
803              
804             =over 4
805              
806             =item o convert gif:- -size 320x85 output.png
807              
808             =item o convert magick:logo -size 320x85 gif:-
809              
810             =back
811              
812             =item o File handle numbers
813              
814             =over 4
815              
816             =item o convert fd:3 png:fd:4 gif:fd:5 fd:6 -append output.png
817              
818             =back
819              
820             =item o The apparently endless variations of the geometry parameter
821              
822             Samples:
823              
824             =over 4
825              
826             =item o 320x85
827              
828             =item o 50%
829              
830             =item o 60%x40
831              
832             =item o 320x85+0+0
833              
834             =item o 60x40%+0+0
835              
836             =item o 50%!+0+0
837              
838             =back
839              
840             =item o Built-in special files
841              
842             Samples:
843              
844             =over 4
845              
846             =item o logo:
847              
848             =item o magick:rose
849              
850             =back
851              
852             =item o Output label format strings
853              
854             =over 4
855              
856             =item o convert magick:rose -label "%wx%h" -format "%l label" rose.png
857              
858             =back
859              
860             =item o The image stack and cloning
861              
862             =over 4
863              
864             =item o convert label.gif ( +clone -shade 110x90 -normalize -negate +clone -compose Plus -composite ) button.gif
865              
866             =item o convert label.gif +clone 0,4,5 button.gif
867              
868             =back
869              
870             =back
871              
872             Imagemagick has a web page, L<http://imagemagick.org/script/command-line-processing.php>, dedicated
873             to the features available in its command line processing code. Please report any cases where this
874             module does not support one of those features. But see L</Trouble-shooting> before reporting an
875             issue, since there I list a few special cases.
876              
877             =head1 Installation
878              
879             Install C<Image::Magick::CommandParser> as you would for any C<Perl> module:
880              
881             Run:
882              
883             cpanm Image::Magick::CommandParser
884              
885             or run:
886              
887             sudo cpan Image::Magick::CommandParser
888              
889             or unpack the distro, and then:
890              
891             perl Makefile.PL
892             make (or dmake or nmake)
893             make test
894             make install
895              
896             =head1 Constructor and Initialization
897              
898             Call C<new()> as C<< my($parser) = Image::Magick::CommandParser -> new(k1 => v1, k2 => v2, ...) >>.
899              
900             It returns a new object of type C<Image::Magick::CommandParser>.
901              
902             Key-value pairs accepted in the parameter list (see also the corresponding methods
903             [e.g. L</command([$string])>]):
904              
905             =over 4
906              
907             =item o command => $string
908              
909             The command string to process.
910              
911             Default: ''.
912              
913             =item o logger => $logger_object
914              
915             Specify a logger object.
916              
917             The default value triggers creation of an object of type L<Log::Handler> which outputs to the
918             screen.
919              
920             To disable logging, just set I<logger> to the empty string.
921              
922             Default: undef.
923              
924             =item o maxlevel => $level
925              
926             This option is only used if an object of type L<Log::Handler> is created. See I<logger> above.
927              
928             See also L<Log::Handler::Levels>.
929              
930             Nothing is printed by default.
931              
932             Default: 'notice'. Typical value is 'debug' and 'info'.
933              
934             =item o minlevel => $level
935              
936             This option is only used if an object of type L<Log::Handler> is created. See I<logger> above.
937              
938             See also L<Log::Handler::Levels>.
939              
940             Default: 'error'.
941              
942             No lower levels are used.
943              
944             =back
945              
946             =head1 Methods
947              
948             =head2 command([$string])
949              
950             Here, the [] indicate an optional parameter.
951              
952             Get or set the command line string to be processed.
953              
954             =head2 log($level, $s)
955              
956             Calls $self -> logger -> log($level => $s) if ($self -> logger).
957              
958             =head2 logger([$logger_object])
959              
960             Here, the [] indicate an optional parameter.
961              
962             Get or set the logger object.
963              
964             To disable logging, just set logger to the empty string.
965              
966             This logger is passed to L<GraphViz2>.
967              
968             Note: C<logger> is a parameter to new().
969              
970             =head2 maxlevel([$string])
971              
972             Here, the [] indicate an optional parameter.
973              
974             Get or set the value used by the logger object.
975              
976             This option is only used if an object of type L<Log::Handler> is created. See
977             L<Log::Handler::Levels>.
978              
979             Note: C<maxlevel> is a parameter to new().
980              
981             =head2 minlevel([$string])
982              
983             Here, the [] indicate an optional parameter.
984              
985             Get or set the value used by the logger object.
986              
987             This option is only used if an object of type L<Log::Handler> is created. See
988             L<Log::Handler::Levels>.
989              
990             Note: C<minlevel> is a parameter to new().
991              
992             =head2 new()
993              
994             The constructor. See L</Constructor and Initialization>.
995              
996             =head2 result()
997              
998             Returns a string of space-separated tokens, as output by the parser.
999              
1000             There is C<result()> in its entirety:
1001              
1002             sub result
1003             {
1004             my($self) = @_;
1005              
1006             return join(' ', map{$$_{token} } $self -> stack -> print);
1007              
1008             } # End of result.
1009              
1010             =head2 run()
1011              
1012             Returns 0 for success and 1 for failure.
1013              
1014             Run the parser on the command provided by C<< new(command => '...') >> or provided by calling
1015             L</command([$string])> before calling C<run()>.
1016              
1017             If the return value is 0, call L</result()> to get a string corresponding to the input, or process
1018             the stack directly by calling L</stack()>.
1019              
1020             Globs etc in the input will be represented by multiple items in the stack.
1021              
1022             =head2 stack()
1023              
1024             This returns an object of type L<Set::Array>, which you can use to iterate over the items output
1025             by the parser.
1026              
1027             See L</result()> just above for how to use this object.
1028              
1029             =head1 FAQ
1030              
1031             =head2 What is the format of stack items?
1032              
1033             They are hashrefs, with these keys:
1034              
1035             =over 4
1036              
1037             =item o token
1038              
1039             This is the token extracted from the command line.
1040              
1041             Note: In the cases of file globbing and redirection of input from a file, these tokens are I<after>
1042             expansion of such items.
1043              
1044             =item o type
1045              
1046             This is my classification of the type of token detected. The values taken by C<type> are:
1047              
1048             =over 4
1049              
1050             =item o action
1051              
1052             =item o close_parenthesis
1053              
1054             =item o command
1055              
1056             =item o done
1057              
1058             In this case, the C<token> will be the empty string.
1059              
1060             =item o input_file
1061              
1062             This is used for both explicit file names and for each file name produced by expanding globs.
1063              
1064             =item o open_parenthesis
1065              
1066             =item o output_file
1067              
1068             =item o operator
1069              
1070             =item o parameter
1071              
1072             =back
1073              
1074             =back
1075              
1076             =head2 Why do you use pairs of states such as 'action' and 'action_1'?
1077              
1078             The way L<Set::FA::Element> was designed, it will not move from a state to the same state when the
1079             input matches. So, to trigger the entry or exit subs, I have to rock back-and-forth between 2
1080             states which are more-or-less identical.
1081              
1082             =head1 Trouble-shooting
1083              
1084             =head2 Installation failure
1085              
1086             I had a failure when installing the module on my laptop for the 1st time. The problem was that,
1087             somehow, during the installation of L<Image::Magick>, root had become the owner of a directory
1088             under the control of perlbrew. To fix this, I had to do:
1089              
1090             sudo chown ron:ron /home/ron/perl5/perlbrew/perls/perl-5.20.2/lib/site_perl/5.20.2/x86_64-linux/auto/Image/Magick
1091              
1092             =head2 Regions specified as '@100000' are not supported
1093              
1094             So, you must put the '@' at the end of the region:
1095              
1096             convert magick:logo -resize '10000@' wiz10000.png
1097              
1098             =head2 Frame references are not supported
1099              
1100             So, this won't work:
1101              
1102             convert 'images.gif[0]' image.png
1103              
1104             =head1 See Also
1105              
1106             L<Imager>
1107              
1108             L<Image::Magick::Chart>
1109              
1110             L<Image::Magick::PolyText>
1111              
1112             L<Image::Magick::Tiler>
1113              
1114             L<Set::Array>
1115              
1116             L<Set::FA::Element>
1117              
1118             =head1 Machine-Readable Change Log
1119              
1120             The file Changes was converted into Changelog.ini by L<Module::Metadata::Changes>.
1121              
1122             =head1 Version Numbers
1123              
1124             Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.
1125              
1126             =head1 Repository
1127              
1128             L<https://github.com/ronsavage/Image-Magick-CommandParser>
1129              
1130             =head1 Support
1131              
1132             Email the author, or log a bug on RT:
1133              
1134             L<https://rt.cpan.org/Public/Dist/Display.html?Name=Image::Magick::CommandParser>.
1135              
1136             =head1 Author
1137              
1138             C<Image::Magick::CommandParser> was written by Ron Savage I<E<lt>ron@savage.net.auE<gt>> in 2016.
1139              
1140             My homepage: L<http://savage.net.au/>
1141              
1142             =head1 Copyright
1143              
1144             Australian copyright (c) 2016, Ron Savage.
1145              
1146             All Programs of mine are 'OSI Certified Open Source Software';
1147             you can redistribute them and/or modify them under the terms of
1148             The Perl License, a copy of which is available at:
1149             http://dev.perl.org/licenses/
1150              
1151             =cut
1152              
1153             __DATA__
1154             @@ built_in_images
1155             granite
1156             logo
1157             netscape
1158             rose
1159             wizard
1160              
1161             @@ built_in_patterns
1162             bricks
1163             checkerboard
1164             circles
1165             crosshatch
1166             crosshatch30
1167             crosshatch45
1168             fishscales
1169             gray0
1170             gray5
1171             gray10
1172             gray15
1173             gray20
1174             gray25
1175             gray30
1176             gray35
1177             gray40
1178             gray45
1179             gray50
1180             gray55
1181             gray60
1182             gray65
1183             gray70
1184             gray75
1185             gray80
1186             gray85
1187             gray90
1188             gray95
1189             gray100
1190             hexagons
1191             horizontal
1192             horizontal2
1193             horizontal3
1194             horizontalsaw
1195             hs_bdiagonal
1196             hs_cross
1197             hs_diagcross
1198             hs_fdiagonal
1199             hs_horizontal
1200             hs_vertical
1201             left30
1202             left45
1203             leftshingle
1204             octagons
1205             right30
1206             right45
1207             rightshingle
1208             smallfishscales
1209             vertical
1210             vertical2
1211             vertical3
1212             verticalbricks
1213             verticalleftshingle
1214             verticalrightshingle
1215             verticalsaw
1216              
1217             @@ image_formats
1218             3fr
1219             aai
1220             ai
1221             art
1222             arw
1223             avi
1224             avs
1225             a
1226             bgra
1227             bgro
1228             bgr
1229             bmp2
1230             bmp3
1231             bmp
1232             brf
1233             b
1234             cals
1235             cal
1236             canvas
1237             caption
1238             cin
1239             cip
1240             clip
1241             cmyka
1242             cmyk
1243             cr2
1244             crw
1245             cur
1246             cut
1247             c
1248             data
1249             dcm
1250             dcr
1251             dcx
1252             dds
1253             dfont
1254             dng
1255             dot
1256             dpx
1257             dxt1
1258             dxt5
1259             epdf
1260             epi
1261             eps
1262             eps2
1263             eps3
1264             epsf
1265             epsi
1266             erf
1267             fax
1268             fits
1269             fractal
1270             fts
1271             g3
1272             gif
1273             gif87
1274             gradient
1275             gray
1276             gv
1277             g
1278             h
1279             hald
1280             hdr
1281             histogram
1282             hrz
1283             html
1284             htm
1285             icb
1286             icon
1287             ico
1288             iiq
1289             info
1290             inline
1291             ipl
1292             isobrl
1293             isobrl6
1294             jng
1295             jnx
1296             jpeg
1297             jpe
1298             jpg
1299             jps
1300             json
1301             k
1302             k25
1303             kdc
1304             label
1305             m
1306             m2v
1307             m4v
1308             mac
1309             magick
1310             map
1311             mask
1312             matte
1313             mat
1314             mef
1315             miff
1316             mkv
1317             mng
1318             mono
1319             mov
1320             mp4
1321             mpc
1322             mpeg
1323             mpg
1324             mrw
1325             msl
1326             msvg
1327             mtv
1328             mvg
1329             nef
1330             nrw
1331             null
1332             orf
1333             otb
1334             otf
1335             o
1336             palm
1337             pal
1338             pam
1339             pango
1340             pattern
1341             pbm
1342             pcds
1343             pcd
1344             pcl
1345             pct
1346             pcx
1347             pdb
1348             pdfa
1349             pdf
1350             pef
1351             pes
1352             pfa
1353             pfb
1354             pfm
1355             pgm
1356             picon
1357             pict
1358             pix
1359             pjpeg
1360             plasma
1361             png00
1362             png24
1363             png32
1364             png48
1365             png64
1366             png8
1367             png
1368             pnm
1369             ppm
1370             preview
1371             ps2
1372             ps3
1373             psb
1374             psd
1375             ps
1376             pwp
1377             radial-gradient
1378             raf
1379             ras
1380             raw
1381             rgba
1382             rgbo
1383             rgb
1384             rgf
1385             rla
1386             rle
1387             rmf
1388             rw2
1389             r
1390             screenshot
1391             scr
1392             sct
1393             sfw
1394             sgi
1395             shtml
1396             sixel
1397             six
1398             sparse-color
1399             sr2
1400             srf
1401             stegano
1402             sun
1403             svgz
1404             svg
1405             text
1406             tga
1407             thumbnail
1408             tile
1409             tim
1410             ttc
1411             ttf
1412             txt
1413             ubrl6
1414             ubrl
1415             uil
1416             uyvy
1417             vda
1418             vicar
1419             vid
1420             viff
1421             vips
1422             vst
1423             wbmp
1424             wmv
1425             wpg
1426             x3f
1427             xbm
1428             xcf
1429             xc
1430             xpm
1431             xps
1432             xv
1433             xwd
1434             x
1435             y
1436             ycbcra
1437             ycbcr
1438             yuv
1439              
1440             @@ pseudo_image_formats
1441             canvas
1442             caption
1443             clip
1444             clipboard
1445             fractal
1446             gradient
1447             hald
1448             histogram
1449             label
1450             map
1451             mask
1452             matte
1453             null
1454             pango
1455             plasma
1456             preview
1457             print
1458             scan
1459             radial_gradient
1460             scanx
1461             screenshot
1462             stegano
1463             tile
1464             unique
1465             vid
1466             win
1467             x