1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %           IIIII  DDDD   EEEEE  N   N  TTTTT  IIIII  FFFFF  Y   Y            %
7 %             I    D   D  E      NN  N    T      I    F       Y Y             %
8 %             I    D   D  EEE    N N N    T      I    FFF      Y              %
9 %             I    D   D  E      N  NN    T      I    F        Y              %
10 %           IIIII  DDDD   EEEEE  N   N    T    IIIII  F        Y              %
11 %                                                                             %
12 %                                                                             %
13 %               Identify an Image Format and Characteristics.                 %
14 %                                                                             %
15 %                           Software Design                                   %
16 %                                Cristy                                       %
17 %                            September 1994                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %  Identify describes the format and characteristics of one or more image
37 %  files.  It will also report if an image is incomplete or corrupt.
38 %
39 %
40 */
41 
42 
43 /*
44   Include declarations.
45 */
46 #include "magick/studio.h"
47 #include "magick/annotate.h"
48 #include "magick/artifact.h"
49 #include "magick/attribute.h"
50 #include "magick/blob.h"
51 #include "magick/cache.h"
52 #include "magick/client.h"
53 #include "magick/coder.h"
54 #include "magick/color.h"
55 #include "magick/configure.h"
56 #include "magick/constitute.h"
57 #include "magick/decorate.h"
58 #include "magick/delegate.h"
59 #include "magick/draw.h"
60 #include "magick/effect.h"
61 #include "magick/exception.h"
62 #include "magick/exception-private.h"
63 #include "magick/feature.h"
64 #include "magick/gem.h"
65 #include "magick/geometry.h"
66 #include "magick/histogram.h"
67 #include "magick/identify.h"
68 #include "magick/image.h"
69 #include "magick/image-private.h"
70 #include "magick/list.h"
71 #include "magick/locale_.h"
72 #include "magick/log.h"
73 #include "magick/magic.h"
74 #include "magick/magick.h"
75 #include "magick/memory_.h"
76 #include "magick/module.h"
77 #include "magick/monitor.h"
78 #include "magick/montage.h"
79 #include "magick/option.h"
80 #include "magick/pixel-private.h"
81 #include "magick/prepress.h"
82 #include "magick/profile.h"
83 #include "magick/property.h"
84 #include "magick/quantize.h"
85 #include "magick/quantum.h"
86 #include "magick/random_.h"
87 #include "magick/registry.h"
88 #include "magick/resize.h"
89 #include "magick/resource_.h"
90 #include "magick/signature.h"
91 #include "magick/statistic.h"
92 #include "magick/string_.h"
93 #include "magick/string-private.h"
94 #include "magick/timer.h"
95 #include "magick/token.h"
96 #include "magick/utility.h"
97 #include "magick/version.h"
98 
99 /*
100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101 %                                                                             %
102 %                                                                             %
103 %                                                                             %
104 %   I d e n t i f y I m a g e                                                 %
105 %                                                                             %
106 %                                                                             %
107 %                                                                             %
108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109 %
110 %  IdentifyImage() identifies an image by printing its attributes to the file.
111 %  Attributes include the image width, height, size, and others.
112 %
113 %  The format of the IdentifyImage method is:
114 %
115 %      MagickBooleanType IdentifyImage(Image *image,FILE *file,
116 %        const MagickBooleanType verbose)
117 %
118 %  A description of each parameter follows:
119 %
120 %    o image: the image.
121 %
122 %    o file: the file, typically stdout.
123 %
124 %    o verbose: A value other than zero prints more detailed information
125 %      about the image.
126 %
127 */
128 
GetLocationStatistics(const Image * image,const StatisticType type,ExceptionInfo * exception)129 static ChannelStatistics *GetLocationStatistics(const Image *image,
130   const StatisticType type,ExceptionInfo *exception)
131 {
132   ChannelStatistics
133     *channel_statistics;
134 
135   ssize_t
136     i;
137 
138   size_t
139     length;
140 
141   ssize_t
142     y;
143 
144   assert(image != (Image *) NULL);
145   assert(image->signature == MagickCoreSignature);
146   if (image->debug != MagickFalse)
147     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
148   length=CompositeChannels+1UL;
149   channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
150     sizeof(*channel_statistics));
151   if (channel_statistics == (ChannelStatistics *) NULL)
152     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
153   (void) memset(channel_statistics,0,length*
154     sizeof(*channel_statistics));
155   for (i=0; i <= (ssize_t) CompositeChannels; i++)
156   {
157     switch (type)
158     {
159       case MaximumStatistic:
160       default:
161       {
162         channel_statistics[i].maxima=(-MagickMaximumValue);
163         break;
164       }
165       case MinimumStatistic:
166       {
167         channel_statistics[i].minima=MagickMaximumValue;
168         break;
169       }
170     }
171   }
172   for (y=0; y < (ssize_t) image->rows; y++)
173   {
174     const IndexPacket
175       *magick_restrict indexes;
176 
177     const PixelPacket
178       *magick_restrict p;
179 
180     ssize_t
181       x;
182 
183     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
184     if (p == (const PixelPacket *) NULL)
185       break;
186     indexes=GetVirtualIndexQueue(image);
187     for (x=0; x < (ssize_t) image->columns; x++)
188     {
189       switch (type)
190       {
191         case MaximumStatistic:
192         default:
193         {
194           if ((double) GetPixelRed(p) > channel_statistics[RedChannel].maxima)
195             channel_statistics[RedChannel].maxima=(double) GetPixelRed(p);
196           if ((double) GetPixelGreen(p) > channel_statistics[GreenChannel].maxima)
197             channel_statistics[GreenChannel].maxima=(double) GetPixelGreen(p);
198           if ((double) GetPixelBlue(p) > channel_statistics[BlueChannel].maxima)
199             channel_statistics[BlueChannel].maxima=(double) GetPixelBlue(p);
200           if ((image->matte != MagickFalse) &&
201               ((double) GetPixelOpacity(p) > channel_statistics[OpacityChannel].maxima))
202             channel_statistics[OpacityChannel].maxima=(double)
203               GetPixelOpacity(p);
204           if ((image->colorspace == CMYKColorspace) &&
205               ((double) GetPixelIndex(indexes+x) > channel_statistics[BlackChannel].maxima))
206             channel_statistics[BlackChannel].maxima=(double)
207               GetPixelIndex(indexes+x);
208           break;
209         }
210         case MinimumStatistic:
211         {
212           if ((double) GetPixelRed(p) < channel_statistics[RedChannel].minima)
213             channel_statistics[RedChannel].minima=(double) GetPixelRed(p);
214           if ((double) GetPixelGreen(p) < channel_statistics[GreenChannel].minima)
215             channel_statistics[GreenChannel].minima=(double) GetPixelGreen(p);
216           if ((double) GetPixelBlue(p) < channel_statistics[BlueChannel].minima)
217             channel_statistics[BlueChannel].minima=(double) GetPixelBlue(p);
218           if ((image->matte != MagickFalse) &&
219               ((double) GetPixelOpacity(p) < channel_statistics[OpacityChannel].minima))
220             channel_statistics[OpacityChannel].minima=(double)
221               GetPixelOpacity(p);
222           if ((image->colorspace == CMYKColorspace) &&
223               ((double) GetPixelIndex(indexes+x) < channel_statistics[BlackChannel].minima))
224             channel_statistics[BlackChannel].minima=(double)
225               GetPixelIndex(indexes+x);
226           break;
227         }
228       }
229       p++;
230     }
231   }
232   return(channel_statistics);
233 }
234 
PrintChannelFeatures(FILE * file,const ChannelType channel,const char * name,const ChannelFeatures * channel_features)235 static ssize_t PrintChannelFeatures(FILE *file,const ChannelType channel,
236   const char *name,const ChannelFeatures *channel_features)
237 {
238 #define PrintFeature(feature) \
239   GetMagickPrecision(),(feature)[0], \
240   GetMagickPrecision(),(feature)[1], \
241   GetMagickPrecision(),(feature)[2], \
242   GetMagickPrecision(),(feature)[3], \
243   GetMagickPrecision(),((feature)[0]+(feature)[1]+(feature)[2]+(feature)[3])/4.0 \
244 
245 #define FeaturesFormat "    %s:\n" \
246   "      Angular Second Moment:\n" \
247   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
248   "      Contrast:\n" \
249   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
250   "      Correlation:\n" \
251   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
252   "      Sum of Squares Variance:\n" \
253   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
254   "      Inverse Difference Moment:\n" \
255   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
256   "      Sum Average:\n" \
257   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
258   "      Sum Variance:\n" \
259   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
260   "      Sum Entropy:\n" \
261   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
262   "      Entropy:\n" \
263   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
264   "      Difference Variance:\n" \
265   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
266   "      Difference Entropy:\n" \
267   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
268   "      Information Measure of Correlation 1:\n" \
269   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
270   "      Information Measure of Correlation 2:\n" \
271   "        %.*g, %.*g, %.*g, %.*g, %.*g\n" \
272   "      Maximum Correlation Coefficient:\n" \
273   "        %.*g, %.*g, %.*g, %.*g, %.*g\n"
274 
275   ssize_t
276     n;
277 
278   n=FormatLocaleFile(file,FeaturesFormat,name,
279     PrintFeature(channel_features[channel].angular_second_moment),
280     PrintFeature(channel_features[channel].contrast),
281     PrintFeature(channel_features[channel].correlation),
282     PrintFeature(channel_features[channel].variance_sum_of_squares),
283     PrintFeature(channel_features[channel].inverse_difference_moment),
284     PrintFeature(channel_features[channel].sum_average),
285     PrintFeature(channel_features[channel].sum_variance),
286     PrintFeature(channel_features[channel].sum_entropy),
287     PrintFeature(channel_features[channel].entropy),
288     PrintFeature(channel_features[channel].difference_variance),
289     PrintFeature(channel_features[channel].difference_entropy),
290     PrintFeature(channel_features[channel].measure_of_correlation_1),
291     PrintFeature(channel_features[channel].measure_of_correlation_2),
292     PrintFeature(channel_features[channel].maximum_correlation_coefficient));
293   return(n);
294 }
295 
PrintChannelLocations(FILE * file,const Image * image,const ChannelType channel,const char * name,const StatisticType type,const size_t max_locations,const ChannelStatistics * channel_statistics)296 static ssize_t PrintChannelLocations(FILE *file,const Image *image,
297   const ChannelType channel,const char *name,const StatisticType type,
298   const size_t max_locations,const ChannelStatistics *channel_statistics)
299 {
300   double
301     target;
302 
303   ExceptionInfo
304     *exception;
305 
306   ssize_t
307     n,
308     y;
309 
310   switch (type)
311   {
312     case MaximumStatistic:
313     default:
314     {
315       target=channel_statistics[channel].maxima;
316       break;
317     }
318     case MeanStatistic:
319     {
320       target=channel_statistics[channel].mean;
321       break;
322     }
323     case MinimumStatistic:
324     {
325       target=channel_statistics[channel].minima;
326       break;
327     }
328   }
329   (void) FormatLocaleFile(file,"  %s: %.*g (%.*g)",name,GetMagickPrecision(),
330     target,GetMagickPrecision(),QuantumScale*target);
331   exception=AcquireExceptionInfo();
332   n=0;
333   for (y=0; y < (ssize_t) image->rows; y++)
334   {
335     const PixelPacket
336       *p;
337 
338     ssize_t
339       x;
340 
341     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
342     if (p == (const PixelPacket *) NULL)
343       break;
344     for (x=0; x < (ssize_t) image->columns; x++)
345     {
346       MagickBooleanType
347         match;
348 
349       match=MagickFalse;
350       switch (channel)
351       {
352         case RedChannel:
353         {
354           match=fabs((double) (p->red-target)) < 0.5 ? MagickTrue : MagickFalse;
355           break;
356         }
357         case GreenChannel:
358         {
359           match=fabs((double) (p->green-target)) < 0.5 ? MagickTrue :
360             MagickFalse;
361           break;
362         }
363         case BlueChannel:
364         {
365           match=fabs((double) (p->blue-target)) < 0.5 ? MagickTrue :
366             MagickFalse;
367           break;
368         }
369         case AlphaChannel:
370         {
371           match=fabs((double) (p->opacity-target)) < 0.5 ? MagickTrue :
372             MagickFalse;
373           break;
374         }
375         default:
376           break;
377       }
378       if (match != MagickFalse)
379         {
380           if ((max_locations != 0) && (n >= (ssize_t) max_locations))
381             break;
382           (void) FormatLocaleFile(file," %.20g,%.20g",(double) x,(double) y);
383           n++;
384         }
385       p++;
386     }
387     if (x < (ssize_t) image->columns)
388       break;
389   }
390   (void) FormatLocaleFile(file,"\n");
391   return(n);
392 }
393 
PrintChannelMoments(FILE * file,const ChannelType channel,const char * name,const double scale,const ChannelMoments * channel_moments)394 static ssize_t PrintChannelMoments(FILE *file,const ChannelType channel,
395   const char *name,const double scale,const ChannelMoments *channel_moments)
396 {
397   double
398     powers[MaximumNumberOfImageMoments] =
399       { 1.0, 2.0, 3.0, 3.0, 6.0, 4.0, 6.0, 4.0 };
400 
401   ssize_t
402     i;
403 
404   ssize_t
405     n;
406 
407   n=FormatLocaleFile(file,"    %s:\n",name);
408   n+=FormatLocaleFile(file,"      Centroid: %.*g,%.*g\n",
409     GetMagickPrecision(),channel_moments[channel].centroid.x,
410     GetMagickPrecision(),channel_moments[channel].centroid.y);
411   n+=FormatLocaleFile(file,"      Ellipse Semi-Major/Minor axis: %.*g,%.*g\n",
412     GetMagickPrecision(),channel_moments[channel].ellipse_axis.x,
413     GetMagickPrecision(),channel_moments[channel].ellipse_axis.y);
414   n+=FormatLocaleFile(file,"      Ellipse angle: %.*g\n",
415     GetMagickPrecision(),channel_moments[channel].ellipse_angle);
416   n+=FormatLocaleFile(file,"      Ellipse eccentricity: %.*g\n",
417     GetMagickPrecision(),channel_moments[channel].ellipse_eccentricity);
418   n+=FormatLocaleFile(file,"      Ellipse intensity: %.*g (%.*g)\n",
419     GetMagickPrecision(),pow(scale,powers[0])*
420     channel_moments[channel].ellipse_intensity,GetMagickPrecision(),
421     channel_moments[channel].ellipse_intensity);
422   for (i=0; i < MaximumNumberOfImageMoments; i++)
423     n+=FormatLocaleFile(file,"      I%.20g: %.*g (%.*g)\n",i+1.0,
424       GetMagickPrecision(),channel_moments[channel].I[i]/pow(scale,powers[i]),
425       GetMagickPrecision(),channel_moments[channel].I[i]);
426   return(n);
427 }
428 
PrintChannelPerceptualHash(FILE * file,const ChannelType channel,const char * name,const ChannelPerceptualHash * channel_phash)429 static ssize_t PrintChannelPerceptualHash(FILE *file,const ChannelType channel,
430   const char *name,const ChannelPerceptualHash *channel_phash)
431 {
432   ssize_t
433     i;
434 
435   ssize_t
436     n;
437 
438   n=FormatLocaleFile(file,"    %s:\n",name);
439   for (i=0; i < MaximumNumberOfPerceptualHashes; i++)
440     n+=FormatLocaleFile(file,"      PH%.20g: %.*g, %.*g\n",i+1.0,
441       GetMagickPrecision(),channel_phash[channel].P[i],
442       GetMagickPrecision(),channel_phash[channel].Q[i]);
443   return(n);
444 }
445 
PrintChannelStatistics(FILE * file,const ChannelType channel,const char * name,const double scale,const ChannelStatistics * channel_statistics)446 static ssize_t PrintChannelStatistics(FILE *file,const ChannelType channel,
447   const char *name,const double scale,
448   const ChannelStatistics *channel_statistics)
449 {
450 #define StatisticsFormat "    %s:\n      min: %.*g  (%.*g)\n      " \
451   "max: %.*g (%.*g)\n      mean: %.*g (%.*g)\n      " \
452   "standard deviation: %.*g (%.*g)\n      kurtosis: %.*g\n      " \
453   "skewness: %.*g\n      entropy: %.*g\n"
454 
455   ssize_t
456     n;
457 
458   n=FormatLocaleFile(file,StatisticsFormat,name,GetMagickPrecision(),
459     (double) ClampToQuantum((MagickRealType) (scale*
460     channel_statistics[channel].minima)),GetMagickPrecision(),
461     channel_statistics[channel].minima/(double) QuantumRange,
462     GetMagickPrecision(),(double) ClampToQuantum((MagickRealType) (scale*
463     channel_statistics[channel].maxima)),GetMagickPrecision(),
464     channel_statistics[channel].maxima/(double) QuantumRange,
465     GetMagickPrecision(),scale*channel_statistics[channel].mean,
466     GetMagickPrecision(),channel_statistics[channel].mean/(double) QuantumRange,
467     GetMagickPrecision(),scale*channel_statistics[channel].standard_deviation,
468     GetMagickPrecision(),channel_statistics[channel].standard_deviation/
469     (double) QuantumRange,GetMagickPrecision(),
470     channel_statistics[channel].kurtosis,GetMagickPrecision(),
471     channel_statistics[channel].skewness,GetMagickPrecision(),
472     channel_statistics[channel].entropy);
473   return(n);
474 }
475 
IdentifyImage(Image * image,FILE * file,const MagickBooleanType verbose)476 MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
477   const MagickBooleanType verbose)
478 {
479   char
480     color[MaxTextExtent],
481     format[MaxTextExtent],
482     key[MaxTextExtent];
483 
484   ChannelFeatures
485     *channel_features;
486 
487   ChannelMoments
488     *channel_moments;
489 
490   ChannelPerceptualHash
491     *channel_phash;
492 
493   ChannelStatistics
494     *channel_statistics;
495 
496   ColorspaceType
497     colorspace;
498 
499   const char
500     *artifact,
501     *locate,
502     *name,
503     *property,
504     *registry,
505     *value;
506 
507   const MagickInfo
508     *magick_info;
509 
510   const PixelPacket
511     *pixels;
512 
513   double
514     elapsed_time,
515     scale,
516     user_time;
517 
518   ExceptionInfo
519     *exception;
520 
521   ImageType
522     type;
523 
524   MagickBooleanType
525     ping;
526 
527   ssize_t
528     i,
529     x;
530 
531   size_t
532     depth,
533     distance;
534 
535   ssize_t
536     y;
537 
538   assert(image != (Image *) NULL);
539   assert(image->signature == MagickCoreSignature);
540   if (image->debug != MagickFalse)
541     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
542   if (file == (FILE *) NULL)
543     file=stdout;
544   exception=AcquireExceptionInfo();
545   colorspace=image->colorspace;
546   locate=GetImageArtifact(image,"identify:locate");
547   if (locate != (const char *) NULL)
548     {
549       const char
550         *limit;
551 
552       size_t
553         max_locations;
554 
555       StatisticType
556         statistic_type;
557 
558       /*
559         Display minimum, maximum, or mean pixel locations.
560       */
561       statistic_type=(StatisticType) ParseCommandOption(MagickStatisticOptions,
562         MagickFalse,locate);
563       limit=GetImageArtifact(image,"identify:limit");
564       max_locations=0;
565       if (limit != (const char *) NULL)
566         max_locations=StringToUnsignedLong(limit);
567       channel_statistics=GetLocationStatistics(image,statistic_type,exception);
568       if (channel_statistics == (ChannelStatistics *) NULL)
569         return(MagickFalse);
570       (void) FormatLocaleFile(file,"  Channel %s locations:\n",locate);
571       switch (colorspace)
572       {
573         case RGBColorspace:
574         default:
575         {
576           (void) PrintChannelLocations(file,image,RedChannel,"Red",
577             statistic_type,max_locations,channel_statistics);
578           (void) PrintChannelLocations(file,image,GreenChannel,"Green",
579             statistic_type,max_locations,channel_statistics);
580           (void) PrintChannelLocations(file,image,BlueChannel,"Blue",
581             statistic_type,max_locations,channel_statistics);
582           break;
583         }
584         case CMYKColorspace:
585         {
586           (void) PrintChannelLocations(file,image,CyanChannel,"Cyan",
587             statistic_type,max_locations,channel_statistics);
588           (void) PrintChannelLocations(file,image,MagentaChannel,"Magenta",
589             statistic_type,max_locations,channel_statistics);
590           (void) PrintChannelLocations(file,image,YellowChannel,"Yellow",
591             statistic_type,max_locations,channel_statistics);
592           (void) PrintChannelLocations(file,image,BlackChannel,"Black",
593             statistic_type,max_locations,channel_statistics);
594           break;
595         }
596         case LinearGRAYColorspace:
597         case GRAYColorspace:
598         {
599           (void) PrintChannelLocations(file,image,GrayChannel,"Gray",
600             statistic_type,max_locations,channel_statistics);
601           break;
602         }
603       }
604       if (image->matte != MagickFalse)
605         (void) PrintChannelLocations(file,image,AlphaChannel,"Alpha",
606           statistic_type,max_locations,channel_statistics);
607       channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
608         channel_statistics);
609       exception=DestroyExceptionInfo(exception);
610       return(ferror(file) != 0 ? MagickFalse : MagickTrue);
611     }
612   *format='\0';
613   elapsed_time=GetElapsedTime(&image->timer);
614   user_time=GetUserTime(&image->timer);
615   GetTimerInfo(&image->timer);
616   if (verbose == MagickFalse)
617     {
618       /*
619         Display summary info about the image.
620       */
621       if (*image->magick_filename != '\0')
622         if (LocaleCompare(image->magick_filename,image->filename) != 0)
623           (void) FormatLocaleFile(file,"%s=>",image->magick_filename);
624       if ((GetPreviousImageInList(image) == (Image *) NULL) &&
625           (GetNextImageInList(image) == (Image *) NULL) && (image->scene == 0))
626         (void) FormatLocaleFile(file,"%s ",image->filename);
627       else
628         (void) FormatLocaleFile(file,"%s[%.20g] ",image->filename,(double)
629           image->scene);
630       (void) FormatLocaleFile(file,"%s ",image->magick);
631       if ((image->magick_columns != 0) || (image->magick_rows != 0))
632         if ((image->magick_columns != image->columns) ||
633             (image->magick_rows != image->rows))
634           (void) FormatLocaleFile(file,"%.20gx%.20g=>",(double)
635             image->magick_columns,(double) image->magick_rows);
636       (void) FormatLocaleFile(file,"%.20gx%.20g ",(double) image->columns,
637         (double) image->rows);
638       if ((image->page.width != 0) || (image->page.height != 0) ||
639           (image->page.x != 0) || (image->page.y != 0))
640         (void) FormatLocaleFile(file,"%.20gx%.20g%+.20g%+.20g ",(double)
641           image->page.width,(double) image->page.height,(double) image->page.x,
642           (double) image->page.y);
643       (void) FormatLocaleFile(file,"%.20g-bit ",(double) image->depth);
644       if (image->type != UndefinedType)
645         (void) FormatLocaleFile(file,"%s ",CommandOptionToMnemonic(
646           MagickTypeOptions,(ssize_t) image->type));
647       if (colorspace != UndefinedColorspace)
648         (void) FormatLocaleFile(file,"%s ",CommandOptionToMnemonic(
649           MagickColorspaceOptions,(ssize_t) colorspace));
650       if (image->storage_class == DirectClass)
651         {
652           if (image->total_colors != 0)
653             {
654               (void) FormatMagickSize(image->total_colors,MagickFalse,format);
655               (void) FormatLocaleFile(file,"%s ",format);
656             }
657         }
658       else
659         if (image->total_colors <= image->colors)
660           (void) FormatLocaleFile(file,"%.20gc ",(double) image->colors);
661         else
662           (void) FormatLocaleFile(file,"%.20g=>%.20gc ",(double)
663             image->total_colors,(double) image->colors);
664       if (image->error.mean_error_per_pixel != 0.0)
665         (void) FormatLocaleFile(file,"%.20g/%f/%fdb ",(double)
666           (image->error.mean_error_per_pixel+0.5),
667           image->error.normalized_mean_error,
668           image->error.normalized_maximum_error);
669       if (image->extent != 0)
670         {
671           (void) FormatMagickSize(image->extent,MagickTrue,format);
672           (void) FormatLocaleFile(file,"%s ",format);
673         }
674       (void) FormatLocaleFile(file,"%0.3fu %lu:%02lu.%03lu",user_time,
675         (unsigned long) (elapsed_time/60.0),(unsigned long) floor(fmod(
676         elapsed_time,60.0)),(unsigned long) (1000.0*(elapsed_time-
677         floor(elapsed_time))));
678       (void) FormatLocaleFile(file,"\n");
679       (void) fflush(file);
680       exception=DestroyExceptionInfo(exception);
681       return(ferror(file) != 0 ? MagickFalse : MagickTrue);
682     }
683   /*
684     Display verbose info about the image.
685   */
686   pixels=GetVirtualPixels(image,0,0,1,1,exception);
687   exception=DestroyExceptionInfo(exception);
688   ping=pixels == (const PixelPacket *) NULL ? MagickTrue : MagickFalse;
689   exception=(&image->exception);
690   (void) SignatureImage(image);
691   channel_statistics=(ChannelStatistics *) NULL;
692   channel_moments=(ChannelMoments *) NULL;
693   channel_phash=(ChannelPerceptualHash *) NULL;
694   channel_features=(ChannelFeatures *) NULL;
695   depth=0;
696   if (ping == MagickFalse)
697     {
698       depth=GetImageDepth(image,exception);
699       channel_statistics=GetImageChannelStatistics(image,exception);
700       if (channel_statistics == (ChannelStatistics *) NULL)
701         return(MagickFalse);
702       artifact=GetImageArtifact(image,"identify:moments");
703       if (artifact != (const char *) NULL)
704         {
705           channel_moments=GetImageChannelMoments(image,exception);
706           channel_phash=GetImageChannelPerceptualHash(image,exception);
707         }
708       artifact=GetImageArtifact(image,"identify:features");
709       if (artifact != (const char *) NULL)
710         {
711           distance=StringToUnsignedLong(artifact);
712           channel_features=GetImageChannelFeatures(image,distance,exception);
713         }
714     }
715   (void) FormatLocaleFile(file,"Image:\n  Filename: %s\n",image->filename);
716   if (*image->magick_filename != '\0')
717     if (LocaleCompare(image->magick_filename,image->filename) != 0)
718       {
719         char
720           filename[MaxTextExtent];
721 
722         GetPathComponent(image->magick_filename,TailPath,filename);
723         (void) FormatLocaleFile(file,"  Base filename: %s\n",filename);
724       }
725   magick_info=GetMagickInfo(image->magick,exception);
726   if ((magick_info == (const MagickInfo *) NULL) ||
727       (GetMagickDescription(magick_info) == (const char *) NULL))
728     (void) FormatLocaleFile(file,"  Format: %s\n",image->magick);
729   else
730     (void) FormatLocaleFile(file,"  Format: %s (%s)\n",image->magick,
731       GetMagickDescription(magick_info));
732   if ((magick_info != (const MagickInfo *) NULL) &&
733       (GetMagickMimeType(magick_info) != (const char *) NULL))
734     (void) FormatLocaleFile(file,"  Mime type: %s\n",GetMagickMimeType(
735       magick_info));
736   (void) FormatLocaleFile(file,"  Class: %s\n",CommandOptionToMnemonic(
737     MagickClassOptions,(ssize_t) image->storage_class));
738   (void) FormatLocaleFile(file,"  Geometry: %.20gx%.20g%+.20g%+.20g\n",(double)
739     image->columns,(double) image->rows,(double) image->tile_offset.x,(double)
740     image->tile_offset.y);
741   if ((image->magick_columns != 0) || (image->magick_rows != 0))
742     if ((image->magick_columns != image->columns) ||
743         (image->magick_rows != image->rows))
744       (void) FormatLocaleFile(file,"  Base geometry: %.20gx%.20g\n",(double)
745         image->magick_columns,(double) image->magick_rows);
746   if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
747     {
748       (void) FormatLocaleFile(file,"  Resolution: %gx%g\n",image->x_resolution,
749         image->y_resolution);
750       (void) FormatLocaleFile(file,"  Print size: %gx%g\n",(double)
751         image->columns/image->x_resolution,(double) image->rows/
752         image->y_resolution);
753     }
754   (void) FormatLocaleFile(file,"  Units: %s\n",CommandOptionToMnemonic(
755     MagickResolutionOptions,(ssize_t) image->units));
756   (void) FormatLocaleFile(file,"  Colorspace: %s\n",CommandOptionToMnemonic(
757     MagickColorspaceOptions,(ssize_t) colorspace));
758   type=IdentifyImageType(image,exception);
759   (void) FormatLocaleFile(file,"  Type: %s\n",CommandOptionToMnemonic(
760     MagickTypeOptions,(ssize_t) type));
761   if (image->type != type)
762     (void) FormatLocaleFile(file,"  Base type: %s\n",CommandOptionToMnemonic(
763       MagickTypeOptions,(ssize_t) image->type));
764   (void) FormatLocaleFile(file,"  Endianness: %s\n",CommandOptionToMnemonic(
765     MagickEndianOptions,(ssize_t) image->endian));
766   if (depth != 0)
767     {
768       if (image->depth == depth)
769         (void) FormatLocaleFile(file,"  Depth: %.20g-bit\n",(double)
770           image->depth);
771       else
772         (void) FormatLocaleFile(file,"  Depth: %.20g/%.20g-bit\n",(double)
773           image->depth,(double) depth);
774     }
775   if (channel_statistics != (ChannelStatistics *) NULL)
776     {
777       /*
778         Detail channel depth and extrema.
779       */
780       (void) FormatLocaleFile(file,"  Channel depth:\n");
781       switch (colorspace)
782       {
783         case RGBColorspace:
784         default:
785         {
786           (void) FormatLocaleFile(file,"    red: %.20g-bit\n",(double)
787             channel_statistics[RedChannel].depth);
788           (void) FormatLocaleFile(file,"    green: %.20g-bit\n",(double)
789             channel_statistics[GreenChannel].depth);
790           (void) FormatLocaleFile(file,"    blue: %.20g-bit\n",(double)
791             channel_statistics[BlueChannel].depth);
792           break;
793         }
794         case CMYKColorspace:
795         {
796           (void) FormatLocaleFile(file,"    cyan: %.20g-bit\n",(double)
797             channel_statistics[CyanChannel].depth);
798           (void) FormatLocaleFile(file,"    magenta: %.20g-bit\n",(double)
799             channel_statistics[MagentaChannel].depth);
800           (void) FormatLocaleFile(file,"    yellow: %.20g-bit\n",(double)
801             channel_statistics[YellowChannel].depth);
802           (void) FormatLocaleFile(file,"    black: %.20g-bit\n",(double)
803             channel_statistics[BlackChannel].depth);
804           break;
805         }
806         case LinearGRAYColorspace:
807         case GRAYColorspace:
808         {
809           (void) FormatLocaleFile(file,"    gray: %.20g-bit\n",(double)
810             channel_statistics[GrayChannel].depth);
811           break;
812         }
813       }
814       if (image->matte != MagickFalse)
815         (void) FormatLocaleFile(file,"    alpha: %.20g-bit\n",(double)
816           channel_statistics[OpacityChannel].depth);
817       scale=1.0;
818       if (image->depth <= MAGICKCORE_QUANTUM_DEPTH)
819         scale=(double) QuantumRange/((size_t) QuantumRange >> ((size_t)
820           MAGICKCORE_QUANTUM_DEPTH-image->depth));
821       (void) FormatLocaleFile(file,"  Channel statistics:\n");
822       (void) FormatLocaleFile(file,"    Pixels: %.20g\n",(double)
823         image->columns*image->rows);
824       switch (colorspace)
825       {
826         case RGBColorspace:
827         default:
828         {
829           (void) PrintChannelStatistics(file,RedChannel,"Red",1.0/scale,
830             channel_statistics);
831           (void) PrintChannelStatistics(file,GreenChannel,"Green",1.0/scale,
832             channel_statistics);
833           (void) PrintChannelStatistics(file,BlueChannel,"Blue",1.0/scale,
834             channel_statistics);
835           break;
836         }
837         case CMYKColorspace:
838         {
839           (void) PrintChannelStatistics(file,CyanChannel,"Cyan",1.0/scale,
840             channel_statistics);
841           (void) PrintChannelStatistics(file,MagentaChannel,"Magenta",1.0/scale,
842             channel_statistics);
843           (void) PrintChannelStatistics(file,YellowChannel,"Yellow",1.0/scale,
844             channel_statistics);
845           (void) PrintChannelStatistics(file,BlackChannel,"Black",1.0/scale,
846             channel_statistics);
847           break;
848         }
849         case LinearGRAYColorspace:
850         case GRAYColorspace:
851         {
852           (void) PrintChannelStatistics(file,GrayChannel,"Gray",1.0/scale,
853             channel_statistics);
854           break;
855         }
856       }
857       if (image->matte != MagickFalse)
858         (void) PrintChannelStatistics(file,AlphaChannel,"Alpha",1.0/scale,
859           channel_statistics);
860       if ((colorspace != LinearGRAYColorspace) && (colorspace != GRAYColorspace))
861         {
862           (void) FormatLocaleFile(file,"  Image statistics:\n");
863           (void) PrintChannelStatistics(file,CompositeChannels,"Overall",1.0/
864             scale,channel_statistics);
865         }
866       channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
867         channel_statistics);
868     }
869   if (channel_moments != (ChannelMoments *) NULL)
870     {
871       scale=(double) ((1UL << image->depth)-1);
872       (void) FormatLocaleFile(file,"  Channel moments:\n");
873       switch (colorspace)
874       {
875         case RGBColorspace:
876         default:
877         {
878           (void) PrintChannelMoments(file,RedChannel,"Red",scale,
879             channel_moments);
880           (void) PrintChannelMoments(file,GreenChannel,"Green",scale,
881             channel_moments);
882           (void) PrintChannelMoments(file,BlueChannel,"Blue",scale,
883             channel_moments);
884           break;
885         }
886         case CMYKColorspace:
887         {
888           (void) PrintChannelMoments(file,CyanChannel,"Cyan",scale,
889             channel_moments);
890           (void) PrintChannelMoments(file,MagentaChannel,"Magenta",scale,
891             channel_moments);
892           (void) PrintChannelMoments(file,YellowChannel,"Yellow",scale,
893             channel_moments);
894           (void) PrintChannelMoments(file,BlackChannel,"Black",scale,
895             channel_moments);
896           break;
897         }
898         case LinearGRAYColorspace:
899         case GRAYColorspace:
900         {
901           (void) PrintChannelMoments(file,GrayChannel,"Gray",scale,
902             channel_moments);
903           break;
904         }
905       }
906       if (image->matte != MagickFalse)
907         (void) PrintChannelMoments(file,AlphaChannel,"Alpha",scale,
908           channel_moments);
909       if ((colorspace != LinearGRAYColorspace) && (colorspace != GRAYColorspace))
910         {
911           (void) FormatLocaleFile(file,"  Image moments:\n");
912           (void) PrintChannelMoments(file,CompositeChannels,"Overall",scale,
913             channel_moments);
914         }
915       channel_moments=(ChannelMoments *) RelinquishMagickMemory(
916         channel_moments);
917     }
918   if (channel_phash != (ChannelPerceptualHash *) NULL)
919     {
920       (void) FormatLocaleFile(file,"  Channel perceptual hash:\n");
921       (void) PrintChannelPerceptualHash(file,RedChannel,"Red, Hue",
922         channel_phash);
923       (void) PrintChannelPerceptualHash(file,GreenChannel,"Green, Chroma",
924         channel_phash);
925       (void) PrintChannelPerceptualHash(file,BlueChannel,"Blue, Luma",
926         channel_phash);
927       if (image->matte != MagickFalse)
928         (void) PrintChannelPerceptualHash(file,AlphaChannel,"Alpha, Alpha",
929           channel_phash);
930       channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
931         channel_phash);
932     }
933   if (channel_features != (ChannelFeatures *) NULL)
934     {
935       (void) FormatLocaleFile(file,"  Channel features (horizontal, vertical, "
936         "left and right diagonals, average):\n");
937       switch (colorspace)
938       {
939         case RGBColorspace:
940         default:
941         {
942           (void) PrintChannelFeatures(file,RedChannel,"Red",channel_features);
943           (void) PrintChannelFeatures(file,GreenChannel,"Green",
944             channel_features);
945           (void) PrintChannelFeatures(file,BlueChannel,"Blue",channel_features);
946           break;
947         }
948         case CMYKColorspace:
949         {
950           (void) PrintChannelFeatures(file,CyanChannel,"Cyan",channel_features);
951           (void) PrintChannelFeatures(file,MagentaChannel,"Magenta",
952             channel_features);
953           (void) PrintChannelFeatures(file,YellowChannel,"Yellow",
954             channel_features);
955           (void) PrintChannelFeatures(file,BlackChannel,"Black",
956             channel_features);
957           break;
958         }
959         case LinearGRAYColorspace:
960         case GRAYColorspace:
961         {
962           (void) PrintChannelFeatures(file,GrayChannel,"Gray",channel_features);
963           break;
964         }
965       }
966       if (image->matte != MagickFalse)
967         (void) PrintChannelFeatures(file,AlphaChannel,"Alpha",channel_features);
968       channel_features=(ChannelFeatures *) RelinquishMagickMemory(
969         channel_features);
970     }
971   if (ping == MagickFalse)
972     {
973       if (colorspace == CMYKColorspace)
974         (void) FormatLocaleFile(file,"  Total ink density: %.*g%%\n",
975           GetMagickPrecision(),100.0*GetImageTotalInkDensity(image)/(double)
976           QuantumRange);
977       x=0;
978       if (image->matte != MagickFalse)
979         {
980           MagickBooleanType
981             found = MagickFalse;
982 
983           const IndexPacket
984             *indexes;
985 
986           const PixelPacket
987             *p;
988 
989           p=(PixelPacket *) NULL;
990           indexes=(IndexPacket *) NULL;
991           for (y=0; y < (ssize_t) image->rows; y++)
992           {
993             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
994             if (p == (const PixelPacket *) NULL)
995               break;
996             indexes=GetVirtualIndexQueue(image);
997             for (x=0; x < (ssize_t) image->columns; x++)
998             {
999               if (GetPixelOpacity(p) == (Quantum) TransparentOpacity)
1000                 {
1001                   found=MagickTrue;
1002                   break;
1003                 }
1004               p++;
1005             }
1006             if (found != MagickFalse)
1007               break;
1008           }
1009           if (found != MagickFalse)
1010             {
1011               char
1012                 tuple[MaxTextExtent];
1013 
1014               MagickPixelPacket
1015                 pixel;
1016 
1017               GetMagickPixelPacket(image,&pixel);
1018               SetMagickPixelPacket(image,p,indexes+x,&pixel);
1019               (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
1020                 exception);
1021               (void) FormatLocaleFile(file,"  Alpha: %s ",tuple);
1022               GetColorTuple(&pixel,MagickTrue,tuple);
1023               (void) FormatLocaleFile(file,"  %s\n",tuple);
1024             }
1025         }
1026       artifact=GetImageArtifact(image,"identify:unique-colors");
1027       if (IsHistogramImage(image,exception) != MagickFalse)
1028         {
1029           (void) FormatLocaleFile(file,"  Colors: %.20g\n",(double)
1030             GetNumberColors(image,(FILE *) NULL,exception));
1031           (void) FormatLocaleFile(file,"  Histogram:\n");
1032           (void) GetNumberColors(image,file,exception);
1033         }
1034       else
1035         if ((artifact != (const char *) NULL) &&
1036             (IsMagickTrue(artifact) != MagickFalse))
1037           (void) FormatLocaleFile(file,"  Colors: %.20g\n",(double)
1038             GetNumberColors(image,(FILE *) NULL,exception));
1039     }
1040   if (image->storage_class == PseudoClass)
1041     {
1042       (void) FormatLocaleFile(file,"  Colormap entries: %.20g\n",(double)
1043         image->colors);
1044       (void) FormatLocaleFile(file,"  Colormap:\n");
1045       if (image->colors <= 1024)
1046         {
1047           char
1048             color[MaxTextExtent],
1049             hex[MaxTextExtent],
1050             tuple[MaxTextExtent];
1051 
1052           MagickPixelPacket
1053             pixel;
1054 
1055           PixelPacket
1056             *magick_restrict p;
1057 
1058           GetMagickPixelPacket(image,&pixel);
1059           p=image->colormap;
1060           for (i=0; i < (ssize_t) image->colors; i++)
1061           {
1062             SetMagickPixelPacket(image,p,(IndexPacket *) NULL,&pixel);
1063             (void) CopyMagickString(tuple,"(",MaxTextExtent);
1064             ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
1065             (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1066             ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
1067             (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1068             ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
1069             if (pixel.colorspace == CMYKColorspace)
1070               {
1071                 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1072                 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,
1073                   tuple);
1074               }
1075             if (pixel.matte != MagickFalse)
1076               {
1077                 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1078                 ConcatenateColorComponent(&pixel,AlphaChannel,X11Compliance,
1079                   tuple);
1080               }
1081             (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1082             (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,
1083               exception);
1084             GetColorTuple(&pixel,MagickTrue,hex);
1085             (void) FormatLocaleFile(file,"    %g: %s %s %s\n",(double) i,tuple,
1086               hex,color);
1087             p++;
1088           }
1089         }
1090     }
1091   if (image->error.mean_error_per_pixel != 0.0)
1092     (void) FormatLocaleFile(file,"  Mean error per pixel: %g\n",
1093       image->error.mean_error_per_pixel);
1094   if (image->error.normalized_mean_error != 0.0)
1095     (void) FormatLocaleFile(file,"  Normalized mean error: %g\n",
1096       image->error.normalized_mean_error);
1097   if (image->error.normalized_maximum_error != 0.0)
1098     (void) FormatLocaleFile(file,"  Normalized maximum error: %g\n",
1099       image->error.normalized_maximum_error);
1100   (void) FormatLocaleFile(file,"  Rendering intent: %s\n",
1101     CommandOptionToMnemonic(MagickIntentOptions,(ssize_t)
1102     image->rendering_intent));
1103   if (image->gamma != 0.0)
1104     (void) FormatLocaleFile(file,"  Gamma: %g\n",image->gamma);
1105   if ((image->chromaticity.red_primary.x != 0.0) ||
1106       (image->chromaticity.green_primary.x != 0.0) ||
1107       (image->chromaticity.blue_primary.x != 0.0) ||
1108       (image->chromaticity.white_point.x != 0.0))
1109     {
1110       /*
1111         Display image chromaticity.
1112       */
1113       (void) FormatLocaleFile(file,"  Chromaticity:\n");
1114       (void) FormatLocaleFile(file,"    red primary: (%g,%g)\n",
1115         image->chromaticity.red_primary.x,image->chromaticity.red_primary.y);
1116       (void) FormatLocaleFile(file,"    green primary: (%g,%g)\n",
1117         image->chromaticity.green_primary.x,
1118         image->chromaticity.green_primary.y);
1119       (void) FormatLocaleFile(file,"    blue primary: (%g,%g)\n",
1120         image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y);
1121       (void) FormatLocaleFile(file,"    white point: (%g,%g)\n",
1122         image->chromaticity.white_point.x,image->chromaticity.white_point.y);
1123     }
1124   if ((image->extract_info.width*image->extract_info.height) != 0)
1125     (void) FormatLocaleFile(file,"  Tile geometry: %.20gx%.20g%+.20g%+.20g\n",
1126       (double) image->extract_info.width,(double) image->extract_info.height,
1127       (double) image->extract_info.x,(double) image->extract_info.y);
1128   (void) QueryColorname(image,&image->background_color,SVGCompliance,color,
1129     exception);
1130   (void) FormatLocaleFile(file,"  Background color: %s\n",color);
1131   (void) QueryColorname(image,&image->border_color,SVGCompliance,color,
1132     exception);
1133   (void) FormatLocaleFile(file,"  Border color: %s\n",color);
1134   (void) QueryColorname(image,&image->matte_color,SVGCompliance,color,
1135     exception);
1136   (void) FormatLocaleFile(file,"  Matte color: %s\n",color);
1137   (void) QueryColorname(image,&image->transparent_color,SVGCompliance,color,
1138     exception);
1139   (void) FormatLocaleFile(file,"  Transparent color: %s\n",color);
1140   (void) FormatLocaleFile(file,"  Interlace: %s\n",CommandOptionToMnemonic(
1141     MagickInterlaceOptions,(ssize_t) image->interlace));
1142   (void) FormatLocaleFile(file,"  Intensity: %s\n",CommandOptionToMnemonic(
1143     MagickPixelIntensityOptions,(ssize_t) image->intensity));
1144   (void) FormatLocaleFile(file,"  Compose: %s\n",CommandOptionToMnemonic(
1145     MagickComposeOptions,(ssize_t) image->compose));
1146   if ((image->page.width != 0) || (image->page.height != 0) ||
1147       (image->page.x != 0) || (image->page.y != 0))
1148     (void) FormatLocaleFile(file,"  Page geometry: %.20gx%.20g%+.20g%+.20g\n",
1149       (double) image->page.width,(double) image->page.height,(double)
1150       image->page.x,(double) image->page.y);
1151   if ((image->page.x != 0) || (image->page.y != 0))
1152     (void) FormatLocaleFile(file,"  Origin geometry: %+.20g%+.20g\n",(double)
1153       image->page.x,(double) image->page.y);
1154   (void) FormatLocaleFile(file,"  Dispose: %s\n",CommandOptionToMnemonic(
1155     MagickDisposeOptions,(ssize_t) image->dispose));
1156   if (image->delay != 0)
1157     (void) FormatLocaleFile(file,"  Delay: %.20gx%.20g\n",(double) image->delay,
1158       (double) image->ticks_per_second);
1159   if (image->iterations != 1)
1160     (void) FormatLocaleFile(file,"  Iterations: %.20g\n",(double)
1161       image->iterations);
1162   if (image->duration != 0)
1163     (void) FormatLocaleFile(file,"  Duration: %.20g\n",(double)
1164       image->duration);
1165   if ((image->next != (Image *) NULL) || (image->previous != (Image *) NULL))
1166     (void) FormatLocaleFile(file,"  Scene: %.20g of %.20g\n",(double)
1167       image->scene,(double) GetImageListLength(image));
1168   else
1169     if (image->scene != 0)
1170       (void) FormatLocaleFile(file,"  Scene: %.20g\n",(double) image->scene);
1171   (void) FormatLocaleFile(file,"  Compression: %s\n",CommandOptionToMnemonic(
1172     MagickCompressOptions,(ssize_t) image->compression));
1173   if (image->quality != UndefinedCompressionQuality)
1174     (void) FormatLocaleFile(file,"  Quality: %.20g\n",(double) image->quality);
1175   (void) FormatLocaleFile(file,"  Orientation: %s\n",CommandOptionToMnemonic(
1176     MagickOrientationOptions,(ssize_t) image->orientation));
1177   if (image->montage != (char *) NULL)
1178     (void) FormatLocaleFile(file,"  Montage: %s\n",image->montage);
1179   if (image->directory != (char *) NULL)
1180     {
1181       Image
1182         *tile;
1183 
1184       ImageInfo
1185         *image_info;
1186 
1187       char
1188         *p,
1189         *q;
1190 
1191       WarningHandler
1192         handler;
1193 
1194       /*
1195         Display visual image directory.
1196       */
1197       image_info=AcquireImageInfo();
1198       (void) CloneString(&image_info->size,"64x64");
1199       (void) FormatLocaleFile(file,"  Directory:\n");
1200       for (p=image->directory; *p != '\0'; p++)
1201       {
1202         q=p;
1203         while ((*q != '\xff') && (*q != '\0') &&
1204                ((size_t) (q-p) < sizeof(image_info->filename)))
1205           q++;
1206         (void) CopyMagickString(image_info->filename,p,(size_t) (q-p+1));
1207         p=q;
1208         (void) FormatLocaleFile(file,"    %s",image_info->filename);
1209         handler=SetWarningHandler((WarningHandler) NULL);
1210         tile=ReadImage(image_info,exception);
1211         (void) SetWarningHandler(handler);
1212         if (tile == (Image *) NULL)
1213           {
1214             (void) FormatLocaleFile(file,"\n");
1215             continue;
1216           }
1217         (void) FormatLocaleFile(file," %.20gx%.20g %s\n",(double)
1218           tile->magick_columns,(double) tile->magick_rows,tile->magick);
1219         (void) SignatureImage(tile);
1220         ResetImagePropertyIterator(tile);
1221         property=GetNextImageProperty(tile);
1222         while (property != (const char *) NULL)
1223         {
1224           (void) FormatLocaleFile(file,"  %s:\n",property);
1225           value=GetImageProperty(tile,property);
1226           if (value != (const char *) NULL)
1227             (void) FormatLocaleFile(file,"%s\n",value);
1228           property=GetNextImageProperty(tile);
1229         }
1230         tile=DestroyImage(tile);
1231       }
1232       image_info=DestroyImageInfo(image_info);
1233     }
1234   (void) FormatLocaleString(key,MaxTextExtent,"8BIM:1999,2998:#1");
1235   value=GetImageProperty(image,key);
1236   if (value != (const char *) NULL)
1237     {
1238       /*
1239         Display clipping path.
1240       */
1241       (void) FormatLocaleFile(file,"  Clipping path: ");
1242       if (strlen(value) > 80)
1243         (void) fputc('\n',file);
1244       (void) FormatLocaleFile(file,"%s\n",value);
1245     }
1246   ResetImageProfileIterator(image);
1247   name=GetNextImageProfile(image);
1248   if (name != (char *) NULL)
1249     {
1250       const StringInfo
1251         *profile;
1252 
1253       /*
1254         Identify image profiles.
1255       */
1256       (void) FormatLocaleFile(file,"  Profiles:\n");
1257       while (name != (char *) NULL)
1258       {
1259         profile=GetImageProfile(image,name);
1260         if (profile == (StringInfo *) NULL)
1261           continue;
1262         (void) FormatLocaleFile(file,"    Profile-%s: %.20g bytes\n",name,
1263           (double) GetStringInfoLength(profile));
1264         if (LocaleCompare(name,"iptc") == 0)
1265           {
1266             char
1267               *attribute,
1268               **attribute_list;
1269 
1270             const char
1271               *tag;
1272 
1273             long
1274               dataset,
1275               record,
1276               sentinel;
1277 
1278             ssize_t
1279               j;
1280 
1281             size_t
1282               length,
1283               profile_length;
1284 
1285             profile_length=GetStringInfoLength(profile);
1286             for (i=0; i < (ssize_t) profile_length-5; i+=(ssize_t) length)
1287             {
1288               length=1;
1289               sentinel=GetStringInfoDatum(profile)[i++];
1290               if (sentinel != 0x1c)
1291                 continue;
1292               dataset=GetStringInfoDatum(profile)[i++];
1293               record=GetStringInfoDatum(profile)[i++];
1294               switch (record)
1295               {
1296                 case 5: tag="Image Name"; break;
1297                 case 7: tag="Edit Status"; break;
1298                 case 10: tag="Priority"; break;
1299                 case 15: tag="Category"; break;
1300                 case 20: tag="Supplemental Category"; break;
1301                 case 22: tag="Fixture Identifier"; break;
1302                 case 25: tag="Keyword"; break;
1303                 case 30: tag="Release Date"; break;
1304                 case 35: tag="Release Time"; break;
1305                 case 40: tag="Special Instructions"; break;
1306                 case 45: tag="Reference Service"; break;
1307                 case 47: tag="Reference Date"; break;
1308                 case 50: tag="Reference Number"; break;
1309                 case 55: tag="Created Date"; break;
1310                 case 60: tag="Created Time"; break;
1311                 case 65: tag="Originating Program"; break;
1312                 case 70: tag="Program Version"; break;
1313                 case 75: tag="Object Cycle"; break;
1314                 case 80: tag="Byline"; break;
1315                 case 85: tag="Byline Title"; break;
1316                 case 90: tag="City"; break;
1317                 case 92: tag="Sub-Location"; break;
1318                 case 95: tag="Province State"; break;
1319                 case 100: tag="Country Code"; break;
1320                 case 101: tag="Country"; break;
1321                 case 103: tag="Original Transmission Reference"; break;
1322                 case 105: tag="Headline"; break;
1323                 case 110: tag="Credit"; break;
1324                 case 115: tag="Src"; break;
1325                 case 116: tag="Copyright String"; break;
1326                 case 120: tag="Caption"; break;
1327                 case 121: tag="Local Caption"; break;
1328                 case 122: tag="Caption Writer"; break;
1329                 case 200: tag="Custom Field 1"; break;
1330                 case 201: tag="Custom Field 2"; break;
1331                 case 202: tag="Custom Field 3"; break;
1332                 case 203: tag="Custom Field 4"; break;
1333                 case 204: tag="Custom Field 5"; break;
1334                 case 205: tag="Custom Field 6"; break;
1335                 case 206: tag="Custom Field 7"; break;
1336                 case 207: tag="Custom Field 8"; break;
1337                 case 208: tag="Custom Field 9"; break;
1338                 case 209: tag="Custom Field 10"; break;
1339                 case 210: tag="Custom Field 11"; break;
1340                 case 211: tag="Custom Field 12"; break;
1341                 case 212: tag="Custom Field 13"; break;
1342                 case 213: tag="Custom Field 14"; break;
1343                 case 214: tag="Custom Field 15"; break;
1344                 case 215: tag="Custom Field 16"; break;
1345                 case 216: tag="Custom Field 17"; break;
1346                 case 217: tag="Custom Field 18"; break;
1347                 case 218: tag="Custom Field 19"; break;
1348                 case 219: tag="Custom Field 20"; break;
1349                 default: tag="unknown"; break;
1350               }
1351               (void) FormatLocaleFile(file,"      %s[%.20g,%.20g]: ",tag,
1352                 (double) dataset,(double) record);
1353               length=(size_t) (GetStringInfoDatum(profile)[i++] << 8);
1354               length|=GetStringInfoDatum(profile)[i++];
1355               length=MagickMin(length,profile_length-i);
1356               attribute=(char *) NULL;
1357               if (~length >= (MaxTextExtent-1))
1358                 attribute=(char *) AcquireQuantumMemory(length+
1359                   MaxTextExtent,sizeof(*attribute));
1360               if (attribute != (char *) NULL)
1361                 {
1362                   (void) CopyMagickString(attribute,(char *)
1363                     GetStringInfoDatum(profile)+i,length+1);
1364                   attribute_list=StringToList(attribute);
1365                   if (attribute_list != (char **) NULL)
1366                     {
1367                       for (j=0; attribute_list[j] != (char *) NULL; j++)
1368                       {
1369                         (void) fputs(attribute_list[j],file);
1370                         (void) fputs("\n",file);
1371                         attribute_list[j]=(char *) RelinquishMagickMemory(
1372                           attribute_list[j]);
1373                       }
1374                       attribute_list=(char **) RelinquishMagickMemory(
1375                         attribute_list);
1376                     }
1377                   attribute=DestroyString(attribute);
1378                 }
1379             }
1380           }
1381         if (image->debug != MagickFalse)
1382           PrintStringInfo(file,name,profile);
1383         name=GetNextImageProfile(image);
1384       }
1385     }
1386   ResetImagePropertyIterator(image);
1387   property=GetNextImageProperty(image);
1388   if (property != (const char *) NULL)
1389     {
1390       /*
1391         Display image properties.
1392       */
1393       (void) FormatLocaleFile(file,"  Properties:\n");
1394       while (property != (const char *) NULL)
1395       {
1396         (void) FormatLocaleFile(file,"    %s: ",property);
1397         value=GetImageProperty(image,property);
1398         if (value != (const char *) NULL)
1399           (void) FormatLocaleFile(file,"%s\n",value);
1400         property=GetNextImageProperty(image);
1401       }
1402     }
1403   ResetImageArtifactIterator(image);
1404   artifact=GetNextImageArtifact(image);
1405   if (artifact != (const char *) NULL)
1406     {
1407       /*
1408         Display image artifacts.
1409       */
1410       (void) FormatLocaleFile(file,"  Artifacts:\n");
1411       while (artifact != (const char *) NULL)
1412       {
1413         (void) FormatLocaleFile(file,"    %s: ",artifact);
1414         value=GetImageArtifact(image,artifact);
1415         if (value != (const char *) NULL)
1416           (void) FormatLocaleFile(file,"%s\n",value);
1417         artifact=GetNextImageArtifact(image);
1418       }
1419     }
1420   ResetImageRegistryIterator();
1421   registry=GetNextImageRegistry();
1422   if (registry != (const char *) NULL)
1423     {
1424       /*
1425         Display image registry.
1426       */
1427       (void) FormatLocaleFile(file,"  Registry:\n");
1428       while (registry != (const char *) NULL)
1429       {
1430         (void) FormatLocaleFile(file,"    %s: ",registry);
1431         value=(const char *) GetImageRegistry(StringRegistryType,registry,
1432           exception);
1433         if (value != (const char *) NULL)
1434           (void) FormatLocaleFile(file,"%s\n",value);
1435         registry=GetNextImageRegistry();
1436       }
1437     }
1438   (void) FormatLocaleFile(file,"  Tainted: %s\n",CommandOptionToMnemonic(
1439     MagickBooleanOptions,(ssize_t) image->taint));
1440   (void) FormatMagickSize(image->extent,MagickTrue,format);
1441   (void) FormatLocaleFile(file,"  Filesize: %s\n",format);
1442   (void) FormatMagickSize((MagickSizeType) image->columns*image->rows,
1443      MagickFalse,format);
1444   if (strlen(format) > 1)
1445     format[strlen(format)-1]='\0';
1446   (void) FormatLocaleFile(file,"  Number pixels: %s\n",format);
1447   if (elapsed_time > MagickEpsilon)
1448     {
1449       (void) FormatMagickSize((MagickSizeType) ((double) image->columns*
1450         image->rows/elapsed_time+0.5),MagickFalse,format);
1451       (void) FormatLocaleFile(file,"  Pixels per second: %s\n",format);
1452     }
1453   (void) FormatLocaleFile(file,"  User time: %0.3fu\n",user_time);
1454   (void) FormatLocaleFile(file,"  Elapsed time: %lu:%02lu.%03lu\n",
1455     (unsigned long) (elapsed_time/60.0),(unsigned long) ceil(fmod(
1456     elapsed_time,60.0)),(unsigned long) (1000.0*(elapsed_time-floor(
1457     elapsed_time))));
1458   (void) FormatLocaleFile(file,"  Version: %s\n",GetMagickVersion((size_t *)
1459     NULL));
1460   (void) fflush(file);
1461   return(ferror(file) != 0 ? MagickFalse : MagickTrue);
1462 }
1463