root / ImageMagick / branches / ImageMagick-6.3.5 / magick / geometry.c

Revision 7647, 41.5 kB (checked in by cristy, 15 months ago)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%           GGGG   EEEEE   OOO   M   M  EEEEE  TTTTT  RRRR   Y   Y            %
7%           G      E      O   O  MM MM  E        T    R   R   Y Y             %
8%           G  GG  EEE    O   O  M M M  EEE      T    RRRR     Y              %
9%           G   G  E      O   O  M   M  E        T    R R      Y              %
10%            GGGG  EEEEE   OOO   M   M  EEEEE    T    R  R     Y              %
11%                                                                             %
12%                                                                             %
13%                       ImageMagick Geometry Methods                          %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                              January 2003                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2005 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%    http://www.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%
37*/
38
39/*
40  Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/constitute.h"
44#include "magick/draw.h"
45#include "magick/exception.h"
46#include "magick/exception-private.h"
47#include "magick/geometry.h"
48#include "magick/memory_.h"
49#include "magick/string_.h"
50#include "magick/token.h"
51
52/*
53%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54%                                                                             %
55%                                                                             %
56%                                                                             %
57%   G e t G e o m e t r y                                                     %
58%                                                                             %
59%                                                                             %
60%                                                                             %
61%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62%
63%  GetGeometry() parses a geometry specification and returns the width,
64%  height, x, and y values.  It also returns flags that indicates which
65%  of the four values (width, height, x, y) were located in the string, and
66%  whether the x or y values are negative.  In addition, there are flags to
67%  report any meta characters (%, !, <, or >).
68%
69%  The format of the GetGeometry method is:
70%
71%      MagickStatusType GetGeometry(const char *geometry,long *x,long *y,
72%        unsigned long *width,unsigned long *height)
73%
74%  A description of each parameter follows:
75%
76%    o geometry:  The geometry.
77%
78%    o x,y:  The x and y offset as determined by the geometry specification.
79%
80%    o width,height:  The width and height as determined by the geometry
81%      specification.
82%
83*/
84MagickExport MagickStatusType GetGeometry(const char *geometry,long *x,long *y,
85  unsigned long *width,unsigned long *height)
86{
87  char
88    *p,
89    pedantic_geometry[MaxTextExtent],
90    *q;
91
92  MagickStatusType
93    flags;
94
95  /*
96    Remove whitespace and meta characters from geometry specification.
97  */
98  flags=NoValue;
99  if ((geometry == (char *) NULL) || (*geometry == '\0'))
100    return(flags);
101  if (strlen(geometry) >= MaxTextExtent)
102    return(flags);
103  (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
104  for (p=pedantic_geometry; *p != '\0'; )
105  {
106    if (isspace((int) ((unsigned char) *p)) != 0)
107      {
108        (void) CopyMagickString(p,p+1,MaxTextExtent);
109        continue;
110      }
111    switch (*p)
112    {
113      case '%':
114      {
115        flags|=PercentValue;
116        (void) CopyMagickString(p,p+1,MaxTextExtent);
117        break;
118      }
119      case '!':
120      {
121        flags|=AspectValue;
122        (void) CopyMagickString(p,p+1,MaxTextExtent);
123        break;
124      }
125      case '<':
126      {
127        flags|=LessValue;
128        (void) CopyMagickString(p,p+1,MaxTextExtent);
129        break;
130      }
131      case '>':
132      {
133        flags|=GreaterValue;
134        (void) CopyMagickString(p,p+1,MaxTextExtent);
135        break;
136      }
137      case '@':
138      {
139        flags|=AreaValue;
140        (void) CopyMagickString(p,p+1,MaxTextExtent);
141        break;
142      }
143      case '(':
144      case ')':
145      {
146        (void) CopyMagickString(p,p+1,MaxTextExtent);
147        break;
148      }
149      case '-':
150      case '.':
151      case '+':
152      case '0':
153      case '1':
154      case '2':
155      case '3':
156      case '4':
157      case '5':
158      case '6':
159      case '7':
160      case '8':
161      case '9':
162      case -41:
163      case 'x':
164      case 'X':
165      {
166        p++;
167        break;
168      }
169      default:
170        return(flags);
171    }
172  }
173  /*
174    Parse width, height, x, and y.
175  */
176  p=pedantic_geometry;
177  if (*p == '\0')
178    return(flags);
179  q=p;
180  (void) strtod(p,&q);
181  if (LocaleNCompare(p,"0x",2) == 0)
182    (void) strtol(p,&q,10);
183  if ((*q == -41) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
184    {
185      /*
186        Parse width.
187      */
188      q=p;
189      if (LocaleNCompare(p,"0x",2) == 0)
190        *width=(unsigned long) strtol(p,&p,10);
191      else
192        *width=(unsigned long) floor(strtod(p,&p)+0.5);
193      if (p != q)
194        flags|=WidthValue;
195    }
196  if ((*p == -41) || (*p == 'x') || (*p == 'X'))
197    {
198      p++;
199      if ((*p != '+') && (*p != '-'))
200        {
201          /*
202            Parse height.
203          */
204          q=p;
205          *height=(unsigned long) floor(strtod(p,&p)+0.5);
206          if (p != q)
207            flags|=HeightValue;
208        }
209    }
210  if ((*p == '+') || (*p == '-'))
211    {
212      /*
213        Parse x value.
214      */
215      if (*p == '-')
216        flags|=XNegative;
217      q=p;
218      *x=(long) ceil(strtod(p,&p)-0.5);
219      if (p != q)
220        flags|=XValue;
221      if ((*p == '+') || (*p == '-'))
222        {
223          /*
224            Parse y value.
225          */
226          if (*p == '-')
227            flags|=YNegative;
228          q=p;
229          *y=(long) ceil(strtod(p,&p)-0.5);
230          if (p != q)
231            flags|=YValue;
232        }
233    }
234  return(flags);
235}
236
237/*
238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239%                                                                             %
240%                                                                             %
241%                                                                             %
242%  G e t P a g e G e o m e t r y                                              %
243%                                                                             %
244%                                                                             %
245%                                                                             %
246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247%
248%  GetPageGeometry() replaces any page mneumonic with the equivalent size in
249%  picas.
250%
251%  The format of the GetPageGeometry method is:
252%
253%      char *GetPageGeometry(const char *page_geometry)
254%
255%  A description of each parameter follows.
256%
257%   o  page_geometry:  Specifies a pointer to an array of characters.
258%      The string is either a Postscript page name (e.g. A4) or a postscript
259%      page geometry (e.g. 612x792+36+36).
260%
261*/
262MagickExport char *GetPageGeometry(const char *page_geometry)
263{
264  static const char
265    *PageSizes[][2]=
266    {
267      { "4x6""288x432" },
268      { "5x7""360x504" },
269      { "7x9""504x648" },
270      { "8x10", "576x720" },
271      { "9x11""648x792" },
272      { "9x12""648x864" },
273      { "10x13""720x936" },
274      { "10x14""720x1008" },
275      { "11x17""792x1224" },
276      { "a0""2384x3370" },
277      { "a1""1684x2384" },
278      { "a10", "73x105" },
279      { "a2""1191x1684" },
280      { "a3""842x1191" },
281      { "a4""595x842" },
282      { "a4smaLL", "595x842" },
283      { "a5""420x595" },
284      { "a6""297x420" },
285      { "a7""210x297" },
286      { "a8""148x210" },
287      { "a9""105x148" },
288      { "archa", "648x864" },
289      { "archb", "864x1296" },
290      { "archC", "1296x1728" },
291      { "archd", "1728x2592" },
292      { "arche", "2592x3456" },
293      { "b0""2920x4127" },
294      { "b1""2064x2920" },
295      { "b10", "91x127" },
296      { "b2""1460x2064" },
297      { "b3""1032x1460" },
298      { "b4""729x1032" },
299      { "b5""516x729" },
300      { "b6""363x516" },
301      { "b7""258x363" },
302      { "b8""181x258" },
303      { "b9""127x181" },
304      { "c0""2599x3676" },
305      { "c1""1837x2599" },
306      { "c2""1298x1837" },
307      { "c3""918x1296" },
308      { "c4""649x918" },
309      { "c5""459x649" },
310      { "c6""323x459" },
311      { "c7""230x323" },
312      { "executive", "540x720" },
313      { "flsa", "612x936" },
314      { "flse", "612x936" },
315      { "folio""612x936" },
316      { "halfletter", "396x612" },
317      { "isob0", "2835x4008" },
318      { "isob1", "2004x2835" },
319      { "isob10", "88x125" },
320      { "isob2", "1417x2004" },
321      { "isob3", "1001x1417" },
322      { "isob4", "709x1001" },
323      { "isob5", "499x709" },
324      { "isob6", "354x499" },
325      { "isob7", "249x354" },
326      { "isob8", "176x249" },
327      { "isob9", "125x176" },
328      { "jisb0", "1030x1456" },
329      { "jisb1", "728x1030" },
330      { "jisb2", "515x728" },
331      { "jisb3", "364x515" },
332      { "jisb4", "257x364" },
333      { "jisb5", "182x257" },
334      { "jisb6", "128x182" },
335      { "ledger""1224x792" },
336      { "legal""612x1008" },
337      { "letter", "612x792" },
338      { "lettersmaLL""612x792" },
339      { "quarto""610x780" },
340      { "statement""396x612" },
341      { "tabloid""792x1224" },
342      { (char *) NULL, (char *) NULL }
343    };
344
345  char
346    *page;
347
348  register long
349    i;
350
351  assert(page_geometry != (char *) NULL);
352  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
353  page=AcquireString(page_geometry);
354  for (i=0; *PageSizes[i] != (char *) NULL; i++)
355    if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
356      {
357        RectangleInfo
358          geometry;
359
360        MagickStatusType
361          flags;
362
363        /*
364          Replace mneumonic with the equivalent size in dots-per-inch.
365        */
366        (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
367        (void) ConcatenateMagickString(page,page_geometry+
368          strlen(PageSizes[i][0]),MaxTextExtent);
369        flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
370          &geometry.height);
371        if ((flags & GreaterValue) == 0)
372          (void) ConcatenateMagickString(page,">",MaxTextExtent);
373        break;
374      }
375  return(page);
376}
377
378/*
379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
380%                                                                             %
381%                                                                             %
382%                                                                             %
383%   G r a v i t y A d j u s t G e o m e t r y                                 %
384%                                                                             %
385%                                                                             %
386%                                                                             %
387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388%
389%  GravityAdjustGeometry() adjusts the offset of a region with regard to the
390%  given: width, height and gravity; against which it is positioned.
391%
392%  The region should also have an appropriate width and height to correctly
393%  set the right offset of the top left corner of the region.
394%
395%  The format of the GravityAdjustGeometry method is:
396%
397%      void ParseGravityGeometry(const unsigned long width,
398%        const unsigned long height,const GravityType gravity,
399%        RectangleInfo *region);
400%
401%  A description of each parameter follows:
402%
403%    o width, height:  the larger area the region is relative to
404%
405%    o gravity: the edge/corner the current offset is relative to
406%
407%    o region:  The region requiring a offset adjustment relative to gravity
408%
409*/
410MagickExport void GravityAdjustGeometry(const unsigned long width,
411  const unsigned long height,const GravityType gravity,RectangleInfo *region)
412{
413  switch (gravity)
414  {
415    case NorthEastGravity:
416    case EastGravity:
417    case SouthEastGravity:
418    {
419      region->x = (long) (width - region->width - region->x);
420      break;
421    }
422    case NorthGravity:
423    case SouthGravity:
424    case CenterGravity:
425    case StaticGravity:
426    {
427      region->x += (long) (width/2 - region->width/2);
428      break;
429    }
430    case ForgetGravity:
431    case NorthWestGravity:
432    case WestGravity:
433    case SouthWestGravity:
434    default:
435      break;
436  }
437  switch (gravity)
438  {
439    case SouthWestGravity:
440    case SouthGravity:
441    case SouthEastGravity:
442    {
443      region->y = (long) (height - region->height - region->y);
444      break;
445    }
446    case EastGravity:
447    case WestGravity:
448    case CenterGravity:
449    case StaticGravity:
450    {
451      region->y += (long) (height/2 - region->height/2);
452      break;
453    }
454    case ForgetGravity:
455    case NorthWestGravity:
456    case NorthGravity:
457    case NorthEastGravity:
458    default:
459      break;
460  }
461  return;
462}
463
464/*
465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466%                                                                             %
467%                                                                             %
468+     I s G e o m e t r y                                                     %
469%                                                                             %
470%                                                                             %
471%                                                                             %
472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473%
474%  IsGeometry() returns MagickTrue if the geometry specification is valid.
475%  Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
476%
477%  The format of the IsGeometry method is:
478%
479%      MagickBooleanType IsGeometry(const char *geometry)
480%
481%  A description of each parameter follows:
482%
483%    o geometry: This string is the geometry specification.
484%
485*/
486MagickExport MagickBooleanType IsGeometry(const char *geometry)
487{
488  GeometryInfo
489    geometry_info;
490
491  MagickStatusType
492    flags;
493
494  if (geometry == (const char *) NULL)
495    return(MagickFalse);
496  flags=ParseGeometry(geometry,&geometry_info);
497  return(flags != NoValue ? MagickTrue : MagickFalse);
498}
499
500/*
501%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
502%                                                                             %
503%                                                                             %
504+     I s S c e n e G e o m e t r y                                           %
505%                                                                             %
506%                                                                             %
507%                                                                             %
508%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509%
510%  IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
511%  specification (e.g. [1], [1-9], [1,7,4]).
512%
513%  The format of the IsSceneGeometry method is:
514%
515%      MagickBooleanType IsSceneGeometry(const char *geometry,
516%        const MagickBooleanType pedantic)
517%
518%  A description of each parameter follows:
519%
520%    o geometry: This string is the geometry specification.
521%
522%    o pedantic: A value other than 0 invokes a more restrictive set of
523%      conditions for a valid specification (e.g. [1], [1-4], [4-1]).
524%
525*/
526MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
527  const MagickBooleanType pedantic)
528{
529  char
530    *p;
531
532  if (geometry == (const char *) NULL)
533    return(MagickFalse);
534  p=(char *) geometry;
535  (void) strtod(geometry,&p);
536  if (p == geometry)
537    return(MagickFalse);
538  if (strspn(geometry,"0123456789-, ") != strlen(geometry))
539    return(MagickFalse);
540  if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
541    return(MagickFalse);
542  return(MagickTrue);
543}
544
545/*
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547%                                                                             %
548%                                                                             %
549%                                                                             %
550%   P a r s e A b s o l u t e G e o m e t r y                                 %
551%                                                                             %
552%                                                                             %
553%                                                                             %
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%
556%  ParseAbsoluteGeometry() returns a region as defined by the geometry string.
557%
558%  The format of the ParseAbsoluteGeometry method is:
559%
560%      MagickStatusType ParseAbsoluteGeometry(const char *geometry,
561%        RectangeInfo *region_info)
562%
563%  A description of each parameter follows:
564%
565%    o geometry:  The geometry (e.g. 100x100+10+10).
566%
567%    o region_info: The region as defined by the geometry string.
568%
569*/
570MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
571  RectangleInfo *region_info)
572{
573  MagickStatusType
574    flags;
575
576  flags=GetGeometry(geometry,&region_info->x,&region_info->y,
577    &region_info->width,&region_info->height);
578  return(flags);
579}
580
581/*
582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
583%                                                                             %
584%                                                                             %
585%                                                                             %
586%   P a r s e A f f i n e G e o m e t r y                                     %
587%                                                                             %
588%                                                                             %
589%                                                                             %
590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
591%
592%  ParseAffineGeometry() returns an affine matrix as defined by the geometry
593%  string.
594%
595%  The format of the ParseAffineGeometry method is:
596%
597%      MagickStatusType ParseAffineGeometry(const char *geometry,
598%        AffineMatrix *affine_matrix)
599%
600%  A description of each parameter follows:
601%
602%    o geometry:  The geometry (e.g. 1.0,0.0,0.0,1.0,3.2,1.2).
603%
604%    o affine_matrix: The affine matrix as defined by the geometry string.
605%
606*/
607
608static inline MagickBooleanType IsPoint(const char *point)
609{
610  char
611    *p;
612
613  (void) strtol(point,&p,10);
614  return(p != point ? MagickTrue : MagickFalse);
615}
616
617MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
618  AffineMatrix *affine_matrix)
619{
620  char
621    token[MaxTextExtent];
622
623  const char
624    *p;
625
626  MagickStatusType
627    flags;
628
629  register long
630    i;
631
632  GetAffineMatrix(affine_matrix);
633  flags=NoValue;
634  p=(char *) geometry;
635  for (i=0; i < 6; i++)
636  {
637    GetMagickToken(p,&p,token);
638    if (*token == ',')
639      GetMagickToken(p,&p,token);
640    switch (i)
641    {
642      case 0: affine_matrix->sx=atof(token); break;
643      case 1: affine_matrix->rx=atof(token); break;
644      case 2: affine_matrix->ry=atof(token); break;
645      case 3: affine_matrix->sy=atof(token); break;
646      case 4: affine_matrix->tx=atof(token); flags|=XValue; break;
647      case 5: affine_matrix->ty=atof(token); flags|=YValue; break;
648    }
649  }
650  return(flags);
651}
652
653/*
654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
655%                                                                             %
656%                                                                             %
657%                                                                             %
658%   P a r s e G e o m e t r y                                                 %
659%                                                                             %
660%                                                                             %
661%                                                                             %
662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663%
664%  ParseGeometry() parses a geometry specification and returns the sigma,
665%  rho, xi, and psi values.  It also returns flags that indicates which
666%  of the four values (sigma, rho, xi, psi) were located in the string, and
667%  whether the xi or pi values are negative.  In addition, there are flags to
668%  report any meta characters (%, !, <, or >).
669%
670%  The format of the ParseGeometry method is:
671%
672%      MagickStatusType ParseGeometry(const char *geometry,
673%        GeometryInfo *geometry_info)
674%
675%  A description of each parameter follows:
676%
677%    o geometry:  The geometry.
678%
679%    o geometry_info:  returns the parsed width/height/x/y in this structure.
680%
681*/
682MagickExport MagickStatusType ParseGeometry(const char *geometry,
683  GeometryInfo *geometry_info)
684{
685  char
686    *p,
687    pedantic_geometry[MaxTextExtent],
688    *q;
689
690  double
691    value;
692
693  MagickStatusType
694    flags;
695
696  /*
697    Remove whitespaces meta characters from geometry specification.
698  */
699  assert(geometry_info != (GeometryInfo *) NULL);
700  flags=NoValue;
701  if ((geometry == (char *) NULL) || (*geometry == '\0'))
702    return(flags);
703  if (strlen(geometry) >= MaxTextExtent)
704    return(flags);
705  (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
706  for (p=pedantic_geometry; *p != '\0'; )
707  {
708    if (isspace((int) ((unsigned char) *p)) != 0)
709      {
710        (void) CopyMagickString(p,p+1,MaxTextExtent);
711        continue;
712      }
713    switch (*p)
714    {
715      case '%':
716      {
717        flags|=PercentValue;
718        (void) CopyMagickString(p,p+1,MaxTextExtent);
719        break;
720      }
721      case '!':
722      {
723        flags|=AspectValue;
724        (void) CopyMagickString(p,p+1,MaxTextExtent);
725        break;
726      }
727      case '<':
728      {
729        flags|=LessValue;
730        (void) CopyMagickString(p,p+1,MaxTextExtent);
731        break;
732      }
733      case '>':
734      {
735        flags|=GreaterValue;
736        (void) CopyMagickString(p,p+1,MaxTextExtent);
737        break;
738      }
739      case '@':
740      {
741        flags|=AreaValue;
742        (void) CopyMagickString(p,p+1,MaxTextExtent);
743        break;
744      }
745      case '(':
746      case ')':
747      {
748        (void) CopyMagickString(p,p+1,MaxTextExtent);
749        break;
750      }
751      case '-':
752      case '+':
753      case '0':
754      case '1':
755      case '2':
756      case '3':
757      case '4':
758      case '5':
759      case '6':
760      case '7':
761      case '8':
762      case '9':
763      case -41:
764      case 'x':
765      case 'X':
766      case ',':
767      case '/':
768      case ':':
769      {
770        p++;
771        break;
772      }
773      case '.':
774      {
775        p++;
776        flags|=DecimalValue;
777        break;
778      }
779      default:
780        return(flags);
781    }
782  }
783  /*
784    Parse rho, sigma, xi, psi, and optionally chi.
785  */
786  p=pedantic_geometry;
787  if (*p == '\0')
788    return(flags);
789  q=p;
790  (void) strtod(p,&q);
791  if (LocaleNCompare(p,"0x",2) == 0)
792    (void) strtol(p,&q,10);
793  if ((*q == -41) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
794      (*q == '/') || (*q == ':') || (*q =='\0'))
795    {
796      /*
797        Parse rho.
798      */
799      q=p;
800      if (LocaleNCompare(p,"0x",2) == 0)
801        value=(double) strtol(p,&p,10);
802      else
803        value=strtod(p,&p);
804      if (p != q)
805        {
806          flags|=RhoValue;
807          geometry_info->rho=value;
808        }
809    }
810  q=p;
811  if ((*p == -41) || (*p == 'x') || (*p == 'X') || (*p == ',') ||
812      (*p == '/') || (*p == ':'))
813    {
814      /*
815        Parse sigma.
816      */
817      p++;
818      while (isspace((int) ((unsigned char) *p)) != 0)
819        p++;
820      if (((*q != -41) && (*q != 'x') && (*q != 'X')) ||
821          ((*p != '+') && (*p != '-')))
822        {
823          q=p;
824          value=strtod(p,&p);
825          if (p != q)
826            {
827              flags|=SigmaValue;
828              geometry_info->sigma=value;
829            }
830        }
831    }
832  while (isspace((int) ((unsigned char) *p)) != 0)
833    p++;
834  if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
835    {
836      /*
837        Parse xi value.
838      */
839      if ((*p == ',') || (*p == '/'))
840        p++;
841      q=p;
842      value=strtod(p,&p);
843      if (p != q)
844        {
845          flags|=XiValue;
846          if (*q == '-')
847            flags|=XiNegative;
848          geometry_info->xi=value;
849        }
850      while (isspace((int) ((unsigned char) *p)) != 0)
851        p++;
852      if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
853          (*p == ':'))
854        {
855          /*
856            Parse psi value.
857          */
858          if ((*p == ',') || (*p == '/'))
859            p++;
860          q=p;
861          value=strtod(p,&p);
862          if (p != q)
863            {
864              flags|=PsiValue;
865              if (*q == '-')
866                flags|=PsiNegative;
867              geometry_info->psi=value;
868            }
869        }
870      while (isspace((int) ((unsigned char) *p)) != 0)
871        p++;
872      if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
873          (*p == ':'))
874        {
875          /*
876            Parse chi value.
877          */
878          if ((*p == ',') || (*p == '/'))
879            p++;
880          q=p;
881          value=strtod(p,&p);
882          if (p != q)
883            {
884              flags|=ChiValue;
885              if (*q == '-')
886                flags|=ChiNegative;
887              geometry_info->chi=value;
888            }
889        }
890    }
891  if (strchr(pedantic_geometry,':') != (char *) NULL)
892    {
893      /*
894        Normalize sampling factor (e.g. 4:2:2 => 2x1).
895      */
896      geometry_info->rho/=geometry_info->sigma;
897      geometry_info->sigma=1.0;
898      if (geometry_info->xi == 0.0)
899        geometry_info->sigma=2.0;
900    }
901  return(flags);
902}
903
904/*
905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
906%                                                                             %
907%                                                                             %
908%                                                                             %
909%   P a r s e G r a v i t y G e o m e t r y                                   %
910%                                                                             %
911%                                                                             %
912%                                                                             %
913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%