Home | History | Annotate | Download | only in MagickCore
      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 %                       MagickCore Geometry Methods                           %
     14 %                                                                             %
     15 %                             Software Design                                 %
     16 %                                  Cristy                                     %
     17 %                              January 2003                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 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 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/constitute.h"
     45 #include "MagickCore/draw.h"
     46 #include "MagickCore/exception.h"
     47 #include "MagickCore/exception-private.h"
     48 #include "MagickCore/geometry.h"
     49 #include "MagickCore/image-private.h"
     50 #include "MagickCore/memory_.h"
     51 #include "MagickCore/string_.h"
     52 #include "MagickCore/string-private.h"
     53 #include "MagickCore/token.h"
     54 
     55 /*
     57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     58 %                                                                             %
     59 %                                                                             %
     60 %                                                                             %
     61 %   G e t G e o m e t r y                                                     %
     62 %                                                                             %
     63 %                                                                             %
     64 %                                                                             %
     65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     66 %
     67 %  GetGeometry() parses a geometry specification and returns the width,
     68 %  height, x, and y values.  It also returns flags that indicates which
     69 %  of the four values (width, height, x, y) were located in the string, and
     70 %  whether the x or y values are negative.  In addition, there are flags to
     71 %  report any meta characters (%, !, <, or >).
     72 %
     73 %  The value must form a proper geometry style specification of WxH+X+Y
     74 %  of integers only, and values can not be separated by comma, colon, or
     75 %  slash charcaters.  See ParseGeometry() below.
     76 %
     77 %  Offsets may be prefixed by multiple signs to make offset string
     78 %  substitutions easier to handle from shell scripts.
     79 %  For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
     80 %  offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
     81 %  offsets.
     82 %
     83 %  The format of the GetGeometry method is:
     84 %
     85 %      MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
     86 %        size_t *width,size_t *height)
     87 %
     88 %  A description of each parameter follows:
     89 %
     90 %    o geometry:  The geometry.
     91 %
     92 %    o x,y:  The x and y offset as determined by the geometry specification.
     93 %
     94 %    o width,height:  The width and height as determined by the geometry
     95 %      specification.
     96 %
     97 */
     98 MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
     99   ssize_t *y,size_t *width,size_t *height)
    100 {
    101   char
    102     *p,
    103     pedantic_geometry[MagickPathExtent],
    104     *q;
    105 
    106   double
    107     value;
    108 
    109   int
    110     c;
    111 
    112   MagickStatusType
    113     flags;
    114 
    115   /*
    116     Remove whitespace and meta characters from geometry specification.
    117   */
    118   flags=NoValue;
    119   if ((geometry == (char *) NULL) || (*geometry == '\0'))
    120     return(flags);
    121   if (strlen(geometry) >= (MagickPathExtent-1))
    122     return(flags);
    123   (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
    124   for (p=pedantic_geometry; *p != '\0'; )
    125   {
    126     if (isspace((int) ((unsigned char) *p)) != 0)
    127       {
    128         (void) CopyMagickString(p,p+1,MagickPathExtent);
    129         continue;
    130       }
    131     c=(int)*p;
    132     switch (c)
    133     {
    134       case '%':
    135       {
    136         flags|=PercentValue;
    137         (void) CopyMagickString(p,p+1,MagickPathExtent);
    138         break;
    139       }
    140       case '!':
    141       {
    142         flags|=AspectValue;
    143         (void) CopyMagickString(p,p+1,MagickPathExtent);
    144         break;
    145       }
    146       case '<':
    147       {
    148         flags|=LessValue;
    149         (void) CopyMagickString(p,p+1,MagickPathExtent);
    150         break;
    151       }
    152       case '>':
    153       {
    154         flags|=GreaterValue;
    155         (void) CopyMagickString(p,p+1,MagickPathExtent);
    156         break;
    157       }
    158       case '^':
    159       {
    160         flags|=MinimumValue;
    161         (void) CopyMagickString(p,p+1,MagickPathExtent);
    162         break;
    163       }
    164       case '@':
    165       {
    166         flags|=AreaValue;
    167         (void) CopyMagickString(p,p+1,MagickPathExtent);
    168         break;
    169       }
    170       case '(':
    171       case ')':
    172       {
    173         (void) CopyMagickString(p,p+1,MagickPathExtent);
    174         break;
    175       }
    176       case 'x':
    177       case 'X':
    178       {
    179         flags|=SeparatorValue;
    180         p++;
    181         break;
    182       }
    183       case '-':
    184       case '.':
    185       case ',':
    186       case '+':
    187       case '0':
    188       case '1':
    189       case '2':
    190       case '3':
    191       case '4':
    192       case '5':
    193       case '6':
    194       case '7':
    195       case '8':
    196       case '9':
    197       case 215:
    198       case 'e':
    199       case 'E':
    200       {
    201         p++;
    202         break;
    203       }
    204       default:
    205         return(flags);
    206     }
    207   }
    208   /*
    209     Parse width, height, x, and y.
    210   */
    211   p=pedantic_geometry;
    212   if (*p == '\0')
    213     return(flags);
    214   q=p;
    215   value=StringToDouble(p,&q);
    216   (void) value;
    217   if (LocaleNCompare(p,"0x",2) == 0)
    218     value=(double) strtol(p,&q,10);
    219   if ((*p != '+') && (*p != '-'))
    220     {
    221       c=(int) ((unsigned char) *q);
    222       if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
    223         {
    224           /*
    225             Parse width.
    226           */
    227           q=p;
    228           if (width != (size_t *) NULL)
    229             {
    230               if (LocaleNCompare(p,"0x",2) == 0)
    231                 *width=(size_t) strtol(p,&p,10);
    232               else
    233                 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
    234             }
    235           if (p != q)
    236             flags|=WidthValue;
    237         }
    238     }
    239   if ((*p != '+') && (*p != '-'))
    240     {
    241       c=(int) ((unsigned char) *p);
    242       if ((c == 215) || (*p == 'x') || (*p == 'X'))
    243         {
    244           p++;
    245           if ((*p != '+') && (*p != '-'))
    246             {
    247               /*
    248                 Parse height.
    249               */
    250               q=p;
    251               if (height != (size_t *) NULL)
    252                 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
    253               if (p != q)
    254                 flags|=HeightValue;
    255             }
    256         }
    257     }
    258   if ((*p == '+') || (*p == '-'))
    259     {
    260       /*
    261         Parse x value.
    262       */
    263       while ((*p == '+') || (*p == '-'))
    264       {
    265         if (*p == '-')
    266           flags^=XNegative;  /* negate sign */
    267         p++;
    268       }
    269       q=p;
    270       if (x != (ssize_t *) NULL)
    271         *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
    272       if (p != q)
    273         {
    274           flags|=XValue;
    275           if (((flags & XNegative) != 0) && (x != (ssize_t *) NULL))
    276             *x=(-*x);
    277         }
    278     }
    279   if ((*p == '+') || (*p == '-'))
    280     {
    281       /*
    282         Parse y value.
    283       */
    284       while ((*p == '+') || (*p == '-'))
    285       {
    286         if (*p == '-')
    287           flags^=YNegative;  /* negate sign */
    288         p++;
    289       }
    290       q=p;
    291       if (y != (ssize_t *) NULL)
    292         *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
    293       if (p != q)
    294         {
    295           flags|=YValue;
    296           if (((flags & YNegative) != 0) && (y != (ssize_t *) NULL))
    297             *y=(-*y);
    298         }
    299     }
    300   if ((flags & PercentValue) != 0)
    301     {
    302       if (((flags & SeparatorValue) == 0) && ((flags & HeightValue) == 0))
    303         {
    304           if ((height != (size_t *) NULL) && (width != (size_t *) NULL))
    305             *height=(*width);
    306           flags|=HeightValue;
    307         }
    308       if (((flags & SeparatorValue) != 0) && ((flags & WidthValue) == 0) &&
    309           (height != (size_t *) NULL) && (width != (size_t *) NULL))
    310             *width=(*height);
    311     }
    312 #if 0
    313   /* Debugging Geometry */
    314   (void) fprintf(stderr,"GetGeometry...\n");
    315   (void) fprintf(stderr,"Input: %s\n",geometry);
    316   (void) fprintf(stderr,"Flags: %c %c %s %s\n",
    317     (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
    318     (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : "  ",
    319     (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : "  ");
    320   (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
    321     *height,(long) *x,(long) *y);
    322 #endif
    323   return(flags);
    324 }
    325 
    326 /*
    328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    329 %                                                                             %
    330 %                                                                             %
    331 %                                                                             %
    332 %  G e t P a g e G e o m e t r y                                              %
    333 %                                                                             %
    334 %                                                                             %
    335 %                                                                             %
    336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    337 %
    338 %  GetPageGeometry() replaces any page mneumonic with the equivalent size in
    339 %  picas.
    340 %
    341 %  The format of the GetPageGeometry method is:
    342 %
    343 %      char *GetPageGeometry(const char *page_geometry)
    344 %
    345 %  A description of each parameter follows.
    346 %
    347 %   o  page_geometry:  Specifies a pointer to an array of characters.  The
    348 %      string is either a Postscript page name (e.g. A4) or a postscript page
    349 %      geometry (e.g. 612x792+36+36).
    350 %
    351 */
    352 MagickExport char *GetPageGeometry(const char *page_geometry)
    353 {
    354 #define MagickPageSize(name,geometry) { (name), sizeof(name)-1, (geometry) }
    355 
    356   typedef struct _PageInfo
    357   {
    358     const char
    359       *name;
    360 
    361     size_t
    362       extent;
    363 
    364     const char
    365       *geometry;
    366   } PageInfo;
    367 
    368   static const PageInfo
    369     PageSizes[] =
    370     {
    371       MagickPageSize("4x6", "288x432"),
    372       MagickPageSize("5x7", "360x504"),
    373       MagickPageSize("7x9", "504x648"),
    374       MagickPageSize("8x10", "576x720"),
    375       MagickPageSize("9x11", "648x792"),
    376       MagickPageSize("9x12", "648x864"),
    377       MagickPageSize("10x13", "720x936"),
    378       MagickPageSize("10x14", "720x1008"),
    379       MagickPageSize("11x17", "792x1224"),
    380       MagickPageSize("a0", "2384x3370"),
    381       MagickPageSize("a1", "1684x2384"),
    382       MagickPageSize("a10", "73x105"),
    383       MagickPageSize("a2", "1191x1684"),
    384       MagickPageSize("a3", "842x1191"),
    385       MagickPageSize("a4", "595x842"),
    386       MagickPageSize("a4small", "595x842"),
    387       MagickPageSize("a5", "420x595"),
    388       MagickPageSize("a6", "297x420"),
    389       MagickPageSize("a7", "210x297"),
    390       MagickPageSize("a8", "148x210"),
    391       MagickPageSize("a9", "105x148"),
    392       MagickPageSize("archa", "648x864"),
    393       MagickPageSize("archb", "864x1296"),
    394       MagickPageSize("archC", "1296x1728"),
    395       MagickPageSize("archd", "1728x2592"),
    396       MagickPageSize("arche", "2592x3456"),
    397       MagickPageSize("b0", "2920x4127"),
    398       MagickPageSize("b1", "2064x2920"),
    399       MagickPageSize("b10", "91x127"),
    400       MagickPageSize("b2", "1460x2064"),
    401       MagickPageSize("b3", "1032x1460"),
    402       MagickPageSize("b4", "729x1032"),
    403       MagickPageSize("b5", "516x729"),
    404       MagickPageSize("b6", "363x516"),
    405       MagickPageSize("b7", "258x363"),
    406       MagickPageSize("b8", "181x258"),
    407       MagickPageSize("b9", "127x181"),
    408       MagickPageSize("c0", "2599x3676"),
    409       MagickPageSize("c1", "1837x2599"),
    410       MagickPageSize("c2", "1298x1837"),
    411       MagickPageSize("c3", "918x1296"),
    412       MagickPageSize("c4", "649x918"),
    413       MagickPageSize("c5", "459x649"),
    414       MagickPageSize("c6", "323x459"),
    415       MagickPageSize("c7", "230x323"),
    416       MagickPageSize("executive", "540x720"),
    417       MagickPageSize("flsa", "612x936"),
    418       MagickPageSize("flse", "612x936"),
    419       MagickPageSize("folio", "612x936"),
    420       MagickPageSize("halfletter", "396x612"),
    421       MagickPageSize("isob0", "2835x4008"),
    422       MagickPageSize("isob1", "2004x2835"),
    423       MagickPageSize("isob10", "88x125"),
    424       MagickPageSize("isob2", "1417x2004"),
    425       MagickPageSize("isob3", "1001x1417"),
    426       MagickPageSize("isob4", "709x1001"),
    427       MagickPageSize("isob5", "499x709"),
    428       MagickPageSize("isob6", "354x499"),
    429       MagickPageSize("isob7", "249x354"),
    430       MagickPageSize("isob8", "176x249"),
    431       MagickPageSize("isob9", "125x176"),
    432       MagickPageSize("jisb0", "1030x1456"),
    433       MagickPageSize("jisb1", "728x1030"),
    434       MagickPageSize("jisb2", "515x728"),
    435       MagickPageSize("jisb3", "364x515"),
    436       MagickPageSize("jisb4", "257x364"),
    437       MagickPageSize("jisb5", "182x257"),
    438       MagickPageSize("jisb6", "128x182"),
    439       MagickPageSize("ledger", "1224x792"),
    440       MagickPageSize("legal", "612x1008"),
    441       MagickPageSize("letter", "612x792"),
    442       MagickPageSize("lettersmall", "612x792"),
    443       MagickPageSize("quarto", "610x780"),
    444       MagickPageSize("statement", "396x612"),
    445       MagickPageSize("tabloid", "792x1224")
    446     };
    447 
    448   char
    449     page[MaxTextExtent];
    450 
    451   register ssize_t
    452     i;
    453 
    454   assert(page_geometry != (char *) NULL);
    455   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
    456   CopyMagickString(page,page_geometry,MaxTextExtent);
    457   for (i=0; i < (ssize_t) (sizeof(PageSizes)/sizeof(PageSizes[0])); i++)
    458   {
    459     int
    460       status;
    461 
    462     status=LocaleNCompare(PageSizes[i].name,page_geometry,PageSizes[i].extent);
    463     if (status == 0)
    464       {
    465         MagickStatusType
    466           flags;
    467 
    468         RectangleInfo
    469           geometry;
    470 
    471         /*
    472           Replace mneumonic with the equivalent size in dots-per-inch.
    473         */
    474         (void) FormatLocaleString(page,MaxTextExtent,"%s%.80s",
    475           PageSizes[i].geometry,page_geometry+PageSizes[i].extent);
    476         flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
    477           &geometry.height);
    478         if ((flags & GreaterValue) == 0)
    479           (void) ConcatenateMagickString(page,">",MaxTextExtent);
    480         break;
    481       }
    482   }
    483   return(AcquireString(page));
    484 }
    485 
    486 /*
    488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    489 %                                                                             %
    490 %                                                                             %
    491 %                                                                             %
    492 %   G r a v i t y A d j u s t G e o m e t r y                                 %
    493 %                                                                             %
    494 %                                                                             %
    495 %                                                                             %
    496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    497 %
    498 %  GravityAdjustGeometry() adjusts the offset of a region with regard to the
    499 %  given: width, height and gravity; against which it is positioned.
    500 %
    501 %  The region should also have an appropriate width and height to correctly
    502 %  set the right offset of the top left corner of the region.
    503 %
    504 %  The format of the GravityAdjustGeometry method is:
    505 %
    506 %      void GravityAdjustGeometry(const size_t width, const size_t height,
    507 %        const GravityType gravity,RectangleInfo *region);
    508 %
    509 %  A description of each parameter follows:
    510 %
    511 %    o width, height:  the larger area the region is relative to
    512 %
    513 %    o gravity: the edge/corner the current offset is relative to
    514 %
    515 %    o region:  The region requiring a offset adjustment relative to gravity
    516 %
    517 */
    518 MagickExport void GravityAdjustGeometry(const size_t width,
    519   const size_t height,const GravityType gravity,RectangleInfo *region)
    520 {
    521   if (region->height == 0)
    522     region->height=height;
    523   if (region->width == 0)
    524     region->width=width;
    525   switch (gravity)
    526   {
    527     case NorthEastGravity:
    528     case EastGravity:
    529     case SouthEastGravity:
    530     {
    531       region->x=(ssize_t) (width-region->width-region->x);
    532       break;
    533     }
    534     case NorthGravity:
    535     case SouthGravity:
    536     case CenterGravity:
    537     {
    538       region->x+=(ssize_t) (width/2-region->width/2);
    539       break;
    540     }
    541     case ForgetGravity:
    542     case NorthWestGravity:
    543     case WestGravity:
    544     case SouthWestGravity:
    545     default:
    546       break;
    547   }
    548   switch (gravity)
    549   {
    550     case SouthWestGravity:
    551     case SouthGravity:
    552     case SouthEastGravity:
    553     {
    554       region->y=(ssize_t) (height-region->height-region->y);
    555       break;
    556     }
    557     case EastGravity:
    558     case WestGravity:
    559     case CenterGravity:
    560     {
    561       region->y+=(ssize_t) (height/2-region->height/2);
    562       break;
    563     }
    564     case ForgetGravity:
    565     case NorthWestGravity:
    566     case NorthGravity:
    567     case NorthEastGravity:
    568     default:
    569       break;
    570   }
    571   return;
    572 }
    573 
    574 /*
    576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    577 %                                                                             %
    578 %                                                                             %
    579 %                                                                             %
    580 +     I s G e o m e t r y                                                     %
    581 %                                                                             %
    582 %                                                                             %
    583 %                                                                             %
    584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    585 %
    586 %  IsGeometry() returns MagickTrue if the geometry specification is valid.
    587 %  Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
    588 %
    589 %  The format of the IsGeometry method is:
    590 %
    591 %      MagickBooleanType IsGeometry(const char *geometry)
    592 %
    593 %  A description of each parameter follows:
    594 %
    595 %    o geometry: This string is the geometry specification.
    596 %
    597 */
    598 MagickExport MagickBooleanType IsGeometry(const char *geometry)
    599 {
    600   GeometryInfo
    601     geometry_info;
    602 
    603   MagickStatusType
    604     flags;
    605 
    606   if (geometry == (const char *) NULL)
    607     return(MagickFalse);
    608   flags=ParseGeometry(geometry,&geometry_info);
    609   return(flags != NoValue ? MagickTrue : MagickFalse);
    610 }
    611 
    612 /*
    614 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    615 %                                                                             %
    616 %                                                                             %
    617 %                                                                             %
    618 +     I s S c e n e G e o m e t r y                                           %
    619 %                                                                             %
    620 %                                                                             %
    621 %                                                                             %
    622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    623 %
    624 %  IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
    625 %  specification (e.g. [1], [1-9], [1,7,4]).
    626 %
    627 %  The format of the IsSceneGeometry method is:
    628 %
    629 %      MagickBooleanType IsSceneGeometry(const char *geometry,
    630 %        const MagickBooleanType pedantic)
    631 %
    632 %  A description of each parameter follows:
    633 %
    634 %    o geometry: This string is the geometry specification.
    635 %
    636 %    o pedantic: A value other than 0 invokes a more restrictive set of
    637 %      conditions for a valid specification (e.g. [1], [1-4], [4-1]).
    638 %
    639 */
    640 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
    641   const MagickBooleanType pedantic)
    642 {
    643   char
    644     *p;
    645 
    646   double
    647     value;
    648 
    649   if (geometry == (const char *) NULL)
    650     return(MagickFalse);
    651   p=(char *) geometry;
    652   value=StringToDouble(geometry,&p);
    653   (void) value;
    654   if (p == geometry)
    655     return(MagickFalse);
    656   if (strspn(geometry,"0123456789-, ") != strlen(geometry))
    657     return(MagickFalse);
    658   if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
    659     return(MagickFalse);
    660   return(MagickTrue);
    661 }
    662 
    663 /*
    665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    666 %                                                                             %
    667 %                                                                             %
    668 %                                                                             %
    669 %   P a r s e A b s o l u t e G e o m e t r y                                 %
    670 %                                                                             %
    671 %                                                                             %
    672 %                                                                             %
    673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    674 %
    675 %  ParseAbsoluteGeometry() returns a region as defined by the geometry string,
    676 %  without any modification by percentages or gravity.
    677 %
    678 %  It currently just a wrapper around GetGeometry(), but may be expanded in
    679 %  the future to handle other positioning information.
    680 %
    681 %  The format of the ParseAbsoluteGeometry method is:
    682 %
    683 %      MagickStatusType ParseAbsoluteGeometry(const char *geometry,
    684 %        RectangleInfo *region_info)
    685 %
    686 %  A description of each parameter follows:
    687 %
    688 %    o geometry:  The geometry string (e.g. "100x100+10+10").
    689 %
    690 %    o region_info: the region as defined by the geometry string.
    691 %
    692 */
    693 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
    694   RectangleInfo *region_info)
    695 {
    696   MagickStatusType
    697     flags;
    698 
    699   flags=GetGeometry(geometry,&region_info->x,&region_info->y,
    700     &region_info->width,&region_info->height);
    701   return(flags);
    702 }
    703 
    704 /*
    706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    707 %                                                                             %
    708 %                                                                             %
    709 %                                                                             %
    710 %   P a r s e A f f i n e G e o m e t r y                                     %
    711 %                                                                             %
    712 %                                                                             %
    713 %                                                                             %
    714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    715 %
    716 %  ParseAffineGeometry() returns an affine matrix as defined by a string of 4
    717 %  to 6 comma/space separated floating point values.
    718 %
    719 %  The affine matrix determinant is checked for validity of the values.
    720 %
    721 %  The format of the ParseAffineGeometry method is:
    722 %
    723 %      MagickStatusType ParseAffineGeometry(const char *geometry,
    724 %        AffineMatrix *affine_matrix,ExceptionInfo *exception)
    725 %
    726 %  A description of each parameter follows:
    727 %
    728 %    o geometry:  The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
    729 %
    730 %    o affine_matrix: the affine matrix as defined by the geometry string.
    731 %
    732 %    o exception: return any errors or warnings in this structure.
    733 %
    734 */
    735 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
    736   AffineMatrix *affine_matrix,ExceptionInfo *exception)
    737 {
    738   char
    739     token[MagickPathExtent];
    740 
    741   const char
    742     *p;
    743 
    744   double
    745     determinant;
    746 
    747   MagickStatusType
    748     flags;
    749 
    750   register ssize_t
    751     i;
    752 
    753   GetAffineMatrix(affine_matrix);
    754   flags=NoValue;
    755   p=(char *) geometry;
    756   for (i=0; (*p != '\0') && (i < 6); i++)
    757   {
    758     GetNextToken(p,&p,MagickPathExtent,token);
    759     if (*token == ',')
    760       GetNextToken(p,&p,MagickPathExtent,token);
    761     switch (i)
    762     {
    763       case 0:
    764       {
    765         affine_matrix->sx=StringToDouble(token,(char **) NULL);
    766         break;
    767       }
    768       case 1:
    769       {
    770         affine_matrix->rx=StringToDouble(token,(char **) NULL);
    771         break;
    772       }
    773       case 2:
    774       {
    775         affine_matrix->ry=StringToDouble(token,(char **) NULL);
    776         break;
    777       }
    778       case 3:
    779       {
    780         affine_matrix->sy=StringToDouble(token,(char **) NULL);
    781         break;
    782       }
    783       case 4:
    784       {
    785         affine_matrix->tx=StringToDouble(token,(char **) NULL);
    786         flags|=XValue;
    787         break;
    788       }
    789       case 5:
    790       {
    791         affine_matrix->ty=StringToDouble(token,(char **) NULL);
    792         flags|=YValue;
    793         break;
    794       }
    795     }
    796   }
    797   determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
    798     affine_matrix->ry);
    799   if (fabs(determinant) < MagickEpsilon)
    800     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
    801       "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
    802   return(flags);
    803 }
    804 
    805 /*
    807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    808 %                                                                             %
    809 %                                                                             %
    810 %                                                                             %
    811 %   P a r s e G e o m e t r y                                                 %
    812 %                                                                             %
    813 %                                                                             %
    814 %                                                                             %
    815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    816 %
    817 %  ParseGeometry() parses a geometry specification and returns the sigma,
    818 %  rho, xi, and psi values.  It also returns flags that indicates which
    819 %  of the four values (sigma, rho, xi, psi) were located in the string, and
    820 %  whether the xi or pi values are negative.
    821 %
    822 %  In addition, it reports if there are any of meta characters (%, !, <, >, @,
    823 %  and ^) flags present. It does not report the location of the percentage
    824 %  relative to the values.
    825 %
    826 %  Values may also be separated by commas, colons, or slashes, and offsets.
    827 %  Offsets may be prefixed by multiple signs to make offset string
    828 %  substitutions easier to handle from shell scripts.
    829 %  For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
    830 %  offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
    831 %  offsets.
    832 %
    833 %  The format of the ParseGeometry method is:
    834 %
    835 %      MagickStatusType ParseGeometry(const char *geometry,
    836 %        GeometryInfo *geometry_info)
    837 %
    838 %  A description of each parameter follows:
    839 %
    840 %    o geometry:  The geometry string (e.g. "100x100+10+10").
    841 %
    842 %    o geometry_info:  returns the parsed width/height/x/y in this structure.
    843 %
    844 */
    845 MagickExport MagickStatusType ParseGeometry(const char *geometry,
    846   GeometryInfo *geometry_info)
    847 {
    848   char
    849     *p,
    850     pedantic_geometry[MagickPathExtent],
    851     *q;
    852 
    853   double
    854     value;
    855 
    856   GeometryInfo
    857     coordinate;
    858 
    859   int
    860     c;
    861 
    862   MagickStatusType
    863     flags;
    864 
    865   /*
    866     Remove whitespaces meta characters from geometry specification.
    867   */
    868   assert(geometry_info != (GeometryInfo *) NULL);
    869   flags=NoValue;
    870   if ((geometry == (char *) NULL) || (*geometry == '\0'))
    871     return(flags);
    872   if (strlen(geometry) >= (MagickPathExtent-1))
    873     return(flags);
    874   c=sscanf(geometry,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",&coordinate.rho,
    875     &coordinate.sigma,&coordinate.xi,&coordinate.psi);
    876   if (c == 4)
    877     {
    878       /*
    879         Special case: coordinate (e.g. 0,0 255,255).
    880       */
    881       geometry_info->rho=coordinate.rho;
    882       geometry_info->sigma=coordinate.sigma;
    883       geometry_info->xi=coordinate.xi;
    884       geometry_info->psi=coordinate.psi;
    885       flags|=RhoValue | SigmaValue | XiValue | PsiValue;
    886       return(flags);
    887     }
    888   (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
    889   for (p=pedantic_geometry; *p != '\0'; )
    890   {
    891     c=(int) ((unsigned char) *p);
    892     if (isspace(c) != 0)
    893       {
    894         (void) CopyMagickString(p,p+1,MagickPathExtent);
    895         continue;
    896       }
    897     switch (c)
    898     {
    899       case '%':
    900       {
    901         flags|=PercentValue;
    902         (void) CopyMagickString(p,p+1,MagickPathExtent);
    903         break;
    904       }
    905       case '!':
    906       {
    907         flags|=AspectValue;
    908         (void) CopyMagickString(p,p+1,MagickPathExtent);
    909         break;
    910       }
    911       case '<':
    912       {
    913         flags|=LessValue;
    914         (void) CopyMagickString(p,p+1,MagickPathExtent);
    915         break;
    916       }
    917       case '>':
    918       {
    919         flags|=GreaterValue;
    920         (void) CopyMagickString(p,p+1,MagickPathExtent);
    921         break;
    922       }
    923       case '^':
    924       {
    925         flags|=MinimumValue;
    926         (void) CopyMagickString(p,p+1,MagickPathExtent);
    927         break;
    928       }
    929       case '@':
    930       {
    931         flags|=AreaValue;
    932         (void) CopyMagickString(p,p+1,MagickPathExtent);
    933         break;
    934       }
    935       case '(':
    936       case ')':
    937       {
    938         (void) CopyMagickString(p,p+1,MagickPathExtent);
    939         break;
    940       }
    941       case 'x':
    942       case 'X':
    943       {
    944         flags|=SeparatorValue;
    945         p++;
    946         break;
    947       }
    948       case '-':
    949       case '+':
    950       case ',':
    951       case '0':
    952       case '1':
    953       case '2':
    954       case '3':
    955       case '4':
    956       case '5':
    957       case '6':
    958       case '7':
    959       case '8':
    960       case '9':
    961       case '/':
    962       case ':':
    963       case 215:
    964       case 'e':
    965       case 'E':
    966       {
    967         p++;
    968         break;
    969       }
    970       case '.':
    971       {
    972         p++;
    973         flags|=DecimalValue;
    974         break;
    975       }
    976       default:
    977         return(NoValue);
    978     }
    979   }
    980   /*
    981     Parse rho, sigma, xi, psi, and optionally chi.
    982   */
    983   p=pedantic_geometry;
    984   if (*p == '\0')
    985     return(flags);
    986   q=p;
    987   value=StringToDouble(p,&q);
    988   if (LocaleNCompare(p,"0x",2) == 0)
    989     (void) strtol(p,&q,10);
    990   c=(int) ((unsigned char) *q);
    991   if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
    992       (*q == '/') || (*q == ':') || (*q =='\0'))
    993     {
    994       /*
    995         Parse rho.
    996       */
    997       q=p;
    998       if (LocaleNCompare(p,"0x",2) == 0)
    999         value=(double) strtol(p,&p,10);
   1000       else
   1001         value=StringToDouble(p,&p);
   1002       if (p != q)
   1003         {
   1004           flags|=RhoValue;
   1005           geometry_info->rho=value;
   1006         }
   1007     }
   1008   q=p;
   1009   c=(int) ((unsigned char) *p);
   1010   if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
   1011       (*p == ':'))
   1012     {
   1013       /*
   1014         Parse sigma.
   1015       */
   1016       p++;
   1017       while (isspace((int) ((unsigned char) *p)) != 0)
   1018         p++;
   1019       c=(int) ((unsigned char) *q);
   1020       if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
   1021           (*p != '-')))
   1022         {
   1023           q=p;
   1024           value=StringToDouble(p,&p);
   1025           if (p != q)
   1026             {
   1027               flags|=SigmaValue;
   1028               geometry_info->sigma=value;
   1029             }
   1030         }
   1031     }
   1032   while (isspace((int) ((unsigned char) *p)) != 0)
   1033     p++;
   1034   if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
   1035     {
   1036       /*
   1037         Parse xi value.
   1038       */
   1039       if ((*p == ',') || (*p == '/') || (*p == ':') )
   1040         p++;
   1041       while ((*p == '+') || (*p == '-'))
   1042       {
   1043         if (*p == '-')
   1044           flags^=XiNegative;  /* negate sign */
   1045         p++;
   1046       }
   1047       q=p;
   1048       value=StringToDouble(p,&p);
   1049       if (p != q)
   1050         {
   1051           flags|=XiValue;
   1052           if ((flags & XiNegative) != 0)
   1053             value=(-value);
   1054           geometry_info->xi=value;
   1055         }
   1056       while (isspace((int) ((unsigned char) *p)) != 0)
   1057         p++;
   1058       if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
   1059           (*p == ':'))
   1060         {
   1061           /*
   1062             Parse psi value.
   1063           */
   1064           if ((*p == ',') || (*p == '/') || (*p == ':'))
   1065             p++;
   1066           while ((*p == '+') || (*p == '-'))
   1067           {
   1068             if (*p == '-')
   1069               flags^=PsiNegative;  /* negate sign */
   1070             p++;
   1071           }
   1072           q=p;
   1073           value=StringToDouble(p,&p);
   1074           if (p != q)
   1075             {
   1076               flags|=PsiValue;
   1077               if ((flags & PsiNegative) != 0)
   1078                 value=(-value);
   1079               geometry_info->psi=value;
   1080             }
   1081       }
   1082       while (isspace((int) ((unsigned char) *p)) != 0)
   1083         p++;
   1084       if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
   1085           (*p == ':'))
   1086         {
   1087           /*
   1088             Parse chi value.
   1089           */
   1090           if ((*p == ',') || (*p == '/') || (*p == ':'))
   1091             p++;
   1092           while ((*p == '+') || (*p == '-'))
   1093           {
   1094             if (*p == '-')
   1095               flags^=ChiNegative;  /* negate sign */
   1096             p++;
   1097           }
   1098           q=p;
   1099           value=StringToDouble(p,&p);
   1100           if (p != q)
   1101             {
   1102               flags|=ChiValue;
   1103               if ((flags & ChiNegative) != 0)
   1104                 value=(-value);
   1105               geometry_info->chi=value;
   1106             }
   1107         }
   1108     }
   1109   if (strchr(pedantic_geometry,':') != (char *) NULL)
   1110     {
   1111       /*
   1112         Normalize sampling factor (e.g. 4:2:2 => 2x1).
   1113       */
   1114       geometry_info->rho/=geometry_info->sigma;
   1115       geometry_info->sigma=1.0;
   1116       if (geometry_info->xi == 0.0)
   1117         geometry_info->sigma=2.0;
   1118     }
   1119   if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
   1120       ((flags & PsiValue) == 0))
   1121     {
   1122       /*
   1123         Support negative height values (e.g. 30x-20).
   1124       */
   1125       geometry_info->sigma=geometry_info->xi;
   1126       geometry_info->xi=0.0;
   1127       flags|=SigmaValue;
   1128       flags&=(~XiValue);
   1129     }
   1130   if ((flags & PercentValue) != 0)
   1131     {
   1132       if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
   1133         geometry_info->sigma=geometry_info->rho;
   1134       if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
   1135         geometry_info->rho=geometry_info->sigma;
   1136     }
   1137 #if 0
   1138   /* Debugging Geometry */
   1139   (void) fprintf(stderr,"ParseGeometry...\n");
   1140   (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
   1141     (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
   1142     (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : "  ",
   1143     (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : "  ",
   1144     (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : "  ");
   1145   (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
   1146     geometry_info->sigma,geometry_info->xi,geometry_info->psi,
   1147     geometry_info->chi);
   1148 #endif
   1149   return(flags);
   1150 }
   1151 
   1152 /*
   1154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1155 %                                                                             %
   1156 %                                                                             %
   1157 %                                                                             %
   1158 %   P a r s e G r a v i t y G e o m e t r y                                   %
   1159 %                                                                             %
   1160 %                                                                             %
   1161 %                                                                             %
   1162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1163 %
   1164 %  ParseGravityGeometry() returns a region as defined by the geometry string
   1165 %  with respect to the given image page (canvas) dimensions and the images
   1166 %  gravity setting.
   1167 %
   1168 %  This is typically used for specifing a area within a given image for
   1169 %  cropping images to a smaller size, chopping out rows and or columns, or
   1170 %  resizing and positioning overlay images.
   1171 %
   1172 %  Percentages are relative to image size and not page size, and are set to
   1173 %  nearest integer (pixel) size.
   1174 %
   1175 %  The format of the ParseGravityGeometry method is:
   1176 %
   1177 %      MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
   1178 %        RectangeInfo *region_info,ExceptionInfo *exception)
   1179 %
   1180 %  A description of each parameter follows:
   1181 %
   1182 %    o geometry:  The geometry string (e.g. "100x100+10+10").
   1183 %
   1184 %    o region_info: the region as defined by the geometry string with respect
   1185 %      to the image dimensions and its gravity.
   1186 %
   1187 %    o exception: return any errors or warnings in this structure.
   1188 %
   1189 */
   1190 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
   1191   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
   1192 {
   1193   MagickStatusType
   1194     flags;
   1195 
   1196   size_t
   1197     height,
   1198     width;
   1199 
   1200   SetGeometry(image,region_info);
   1201   if (image->page.width != 0)
   1202     region_info->width=image->page.width;
   1203   if (image->page.height != 0)
   1204     region_info->height=image->page.height;
   1205   flags=ParseAbsoluteGeometry(geometry,region_info);
   1206   if (flags == NoValue)
   1207     {
   1208       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1209         "InvalidGeometry","`%s'",geometry);
   1210       return(flags);
   1211     }
   1212   if ((flags & PercentValue) != 0)
   1213     {
   1214       GeometryInfo
   1215         geometry_info;
   1216 
   1217       MagickStatusType
   1218         status;
   1219 
   1220       PointInfo
   1221         scale;
   1222 
   1223       /*
   1224         Geometry is a percentage of the image size, not canvas size
   1225       */
   1226       if (image->gravity != UndefinedGravity)
   1227         flags|=XValue | YValue;
   1228       status=ParseGeometry(geometry,&geometry_info);
   1229       scale.x=geometry_info.rho;
   1230       if ((status & RhoValue) == 0)
   1231         scale.x=100.0;
   1232       scale.y=geometry_info.sigma;
   1233       if ((status & SigmaValue) == 0)
   1234         scale.y=scale.x;
   1235       region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
   1236       region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
   1237     }
   1238   /*
   1239     Adjust offset according to gravity setting.
   1240   */
   1241   width=region_info->width;
   1242   height=region_info->height;
   1243   if (width == 0)
   1244     region_info->width=image->page.width | image->columns;
   1245   if (height == 0)
   1246     region_info->height=image->page.height | image->rows;
   1247   GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
   1248   region_info->width=width;
   1249   region_info->height=height;
   1250   return(flags);
   1251 }
   1252 
   1253 /*
   1255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1256 %                                                                             %
   1257 %                                                                             %
   1258 %                                                                             %
   1259 +   P a r s e M e t a G e o m e t r y                                         %
   1260 %                                                                             %
   1261 %                                                                             %
   1262 %                                                                             %
   1263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1264 %
   1265 %  ParseMetaGeometry() is similar to GetGeometry() except the returned
   1266 %  geometry is modified as determined by the meta characters:  %, !, <, >, @,
   1267 %  and ^ in relation to image resizing.
   1268 %
   1269 %  Final image dimensions are adjusted so as to preserve the aspect ratio as
   1270 %  much as possible, while generating a integer (pixel) size, and fitting the
   1271 %  image within the specified geometry width and height.
   1272 %
   1273 %  Flags are interpreted...
   1274 %     %   geometry size is given percentage of original width and height given
   1275 %     !   do not try to preserve aspect ratio
   1276 %     <   only enlarge images smaller that geometry
   1277 %     >   only shrink images larger than geometry
   1278 %     @   Fit image to contain at most this many pixels
   1279 %     ^   Contain the given geometry given, (minimal dimensions given)
   1280 %
   1281 %  The format of the ParseMetaGeometry method is:
   1282 %
   1283 %      MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
   1284 %        ssize_t *y, size_t *width,size_t *height)
   1285 %
   1286 %  A description of each parameter follows:
   1287 %
   1288 %    o geometry:  The geometry string (e.g. "100x100+10+10").
   1289 %
   1290 %    o x,y:  The x and y offset, set according to the geometry specification.
   1291 %
   1292 %    o width,height:  The width and height of original image, modified by
   1293 %      the given geometry specification.
   1294 %
   1295 */
   1296 
   1297 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
   1298   ssize_t *y,size_t *width,size_t *height)
   1299 {
   1300   GeometryInfo
   1301     geometry_info;
   1302 
   1303   MagickStatusType
   1304     flags;
   1305 
   1306   size_t
   1307     former_height,
   1308     former_width;
   1309 
   1310   /*
   1311     Ensure the image geometry is valid.
   1312   */
   1313   assert(x != (ssize_t *) NULL);
   1314   assert(y != (ssize_t *) NULL);
   1315   assert(width != (size_t *) NULL);
   1316   assert(height != (size_t *) NULL);
   1317   if ((geometry == (char *) NULL) || (*geometry == '\0'))
   1318     return(NoValue);
   1319   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
   1320   /*
   1321     Parse geometry using GetGeometry.
   1322   */
   1323   SetGeometryInfo(&geometry_info);
   1324   former_width=(*width);
   1325   former_height=(*height);
   1326   flags=GetGeometry(geometry,x,y,width,height);
   1327   if ((flags & PercentValue) != 0)
   1328     {
   1329       MagickStatusType
   1330         flags;
   1331 
   1332       PointInfo
   1333         scale;
   1334 
   1335       /*
   1336         Geometry is a percentage of the image size.
   1337       */
   1338       flags=ParseGeometry(geometry,&geometry_info);
   1339       scale.x=geometry_info.rho;
   1340       if ((flags & RhoValue) == 0)
   1341         scale.x=100.0;
   1342       scale.y=geometry_info.sigma;
   1343       if ((flags & SigmaValue) == 0)
   1344         scale.y=scale.x;
   1345       *width=(size_t) floor(scale.x*former_width/100.0+0.5);
   1346       *height=(size_t) floor(scale.y*former_height/100.0+0.5);
   1347       former_width=(*width);
   1348       former_height=(*height);
   1349     }
   1350   if (((flags & AspectValue) != 0) || ((*width == former_width) &&
   1351       (*height == former_height)))
   1352     {
   1353       if ((flags & RhoValue) == 0)
   1354         *width=former_width;
   1355       if ((flags & SigmaValue) == 0)
   1356         *height=former_height;
   1357     }
   1358   else
   1359     {
   1360       double
   1361         scale_factor;
   1362 
   1363       /*
   1364         Respect aspect ratio of the image.
   1365       */
   1366       if ((former_width == 0) || (former_height == 0))
   1367         scale_factor=1.0;
   1368       else
   1369         if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
   1370           {
   1371             scale_factor=(double) *width/(double) former_width;
   1372             if ((flags & MinimumValue) == 0)
   1373               {
   1374                 if (scale_factor > ((double) *height/(double) former_height))
   1375                   scale_factor=(double) *height/(double) former_height;
   1376               }
   1377             else
   1378               if (scale_factor < ((double) *height/(double) former_height))
   1379                 scale_factor=(double) *height/(double) former_height;
   1380           }
   1381         else
   1382           if ((flags & RhoValue) != 0)
   1383             {
   1384               scale_factor=(double) *width/(double) former_width;
   1385               if (((flags & MinimumValue) != 0) &&
   1386                   (scale_factor < ((double) *width/(double) former_height)))
   1387                 scale_factor=(double) *width/(double) former_height;
   1388             }
   1389           else
   1390             {
   1391               scale_factor=(double) *height/(double) former_height;
   1392               if (((flags & MinimumValue) != 0) &&
   1393                   (scale_factor < ((double) *height/(double) former_width)))
   1394                 scale_factor=(double) *height/(double) former_width;
   1395             }
   1396       *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
   1397       *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
   1398     }
   1399   if ((flags & GreaterValue) != 0)
   1400     {
   1401       if (former_width < *width)
   1402         *width=former_width;
   1403       if (former_height < *height)
   1404         *height=former_height;
   1405     }
   1406   if ((flags & LessValue) != 0)
   1407     {
   1408       if (former_width > *width)
   1409         *width=former_width;
   1410       if (former_height > *height)
   1411         *height=former_height;
   1412     }
   1413   if ((flags & AreaValue) != 0)
   1414     {
   1415       double
   1416         area,
   1417         distance;
   1418 
   1419       PointInfo
   1420         scale;
   1421 
   1422       /*
   1423         Geometry is a maximum area in pixels.
   1424       */
   1425       (void) ParseGeometry(geometry,&geometry_info);
   1426       area=geometry_info.rho+sqrt(MagickEpsilon);
   1427       distance=sqrt((double) former_width*former_height);
   1428       scale.x=(double) former_width/(distance/sqrt(area));
   1429       scale.y=(double) former_height/(distance/sqrt(area));
   1430       if ((scale.x < (double) *width) || (scale.y < (double) *height))
   1431         {
   1432           *width=(unsigned long) (former_width/(distance/sqrt(area)));
   1433           *height=(unsigned long) (former_height/(distance/sqrt(area)));
   1434         }
   1435       former_width=(*width);
   1436       former_height=(*height);
   1437     }
   1438   return(flags);
   1439 }
   1440 
   1441 /*
   1443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1444 %                                                                             %
   1445 %                                                                             %
   1446 %                                                                             %
   1447 %   P a r s e P a g e G e o m e t r y                                         %
   1448 %                                                                             %
   1449 %                                                                             %
   1450 %                                                                             %
   1451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1452 %
   1453 %  ParsePageGeometry() returns a region as defined by the geometry string with
   1454 %  respect to the image page (canvas) dimensions.
   1455 %
   1456 %  WARNING: Percentage dimensions remain relative to the actual image
   1457 %  dimensions, and not canvas dimensions.
   1458 %
   1459 %  The format of the ParsePageGeometry method is:
   1460 %
   1461 %      MagickStatusType ParsePageGeometry(const Image *image,
   1462 %        const char *geometry,RectangeInfo *region_info,
   1463 %        ExceptionInfo *exception)
   1464 %
   1465 %  A description of each parameter follows:
   1466 %
   1467 %    o geometry:  The geometry string (e.g. "100x100+10+10").
   1468 %
   1469 %    o region_info: the region as defined by the geometry string with
   1470 %      respect to the image and its gravity.
   1471 %
   1472 %    o exception: return any errors or warnings in this structure.
   1473 %
   1474 */
   1475 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
   1476   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
   1477 {
   1478   MagickStatusType
   1479     flags;
   1480 
   1481   SetGeometry(image,region_info);
   1482   if (image->page.width != 0)
   1483     region_info->width=image->page.width;
   1484   if (image->page.height != 0)
   1485     region_info->height=image->page.height;
   1486   flags=ParseAbsoluteGeometry(geometry,region_info);
   1487   if (flags == NoValue)
   1488     {
   1489       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1490         "InvalidGeometry","`%s'",geometry);
   1491       return(flags);
   1492     }
   1493   if ((flags & PercentValue) != 0)
   1494     {
   1495       region_info->width=image->columns;
   1496       region_info->height=image->rows;
   1497     }
   1498   flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
   1499     &region_info->width,&region_info->height);
   1500   if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
   1501       (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
   1502     {
   1503       if ((flags & WidthValue) == 0)
   1504         region_info->width=region_info->height;
   1505       if ((flags & HeightValue) == 0)
   1506         region_info->height=region_info->width;
   1507     }
   1508   return(flags);
   1509 }
   1510 
   1511 /*
   1513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1514 %                                                                             %
   1515 %                                                                             %
   1516 %                                                                             %
   1517 %   P a r s e R e g i o n G e o m e t r y                                     %
   1518 %                                                                             %
   1519 %                                                                             %
   1520 %                                                                             %
   1521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1522 %
   1523 %  ParseRegionGeometry() returns a region as defined by the geometry string
   1524 %  with respect to the image dimensions and aspect ratio.
   1525 %
   1526 %  This is basically a wrapper around ParseMetaGeometry.  This is typically
   1527 %  used to parse a geometry string to work out the final integer dimensions
   1528 %  for image resizing.
   1529 %
   1530 %  The format of the ParseRegionGeometry method is:
   1531 %
   1532 %      MagickStatusType ParseRegionGeometry(const Image *image,
   1533 %        const char *geometry,RectangeInfo *region_info,
   1534 %        ExceptionInfo *exception)
   1535 %
   1536 %  A description of each parameter follows:
   1537 %
   1538 %    o geometry:  The geometry string (e.g. "100x100+10+10").
   1539 %
   1540 %    o region_info: the region as defined by the geometry string.
   1541 %
   1542 %    o exception: return any errors or warnings in this structure.
   1543 %
   1544 */
   1545 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
   1546   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
   1547 {
   1548   MagickStatusType
   1549     flags;
   1550 
   1551   SetGeometry(image,region_info);
   1552   flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
   1553     &region_info->width,&region_info->height);
   1554   if (flags == NoValue)
   1555     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1556       "InvalidGeometry","`%s'",geometry);
   1557   return(flags);
   1558 }
   1559 
   1560 /*
   1562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1563 %                                                                             %
   1564 %                                                                             %
   1565 %                                                                             %
   1566 %   S e t G e o m e t r y                                                     %
   1567 %                                                                             %
   1568 %                                                                             %
   1569 %                                                                             %
   1570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1571 %
   1572 %  SetGeometry() sets the geometry to its default values.
   1573 %
   1574 %  The format of the SetGeometry method is:
   1575 %
   1576 %      SetGeometry(const Image *image,RectangleInfo *geometry)
   1577 %
   1578 %  A description of each parameter follows:
   1579 %
   1580 %    o image: the image.
   1581 %
   1582 %    o geometry: the geometry.
   1583 %
   1584 */
   1585 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
   1586 {
   1587   assert(image != (Image *) NULL);
   1588   assert(image->signature == MagickCoreSignature);
   1589   if (image->debug != MagickFalse)
   1590     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1591   assert(geometry != (RectangleInfo *) NULL);
   1592   (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
   1593   geometry->width=image->columns;
   1594   geometry->height=image->rows;
   1595 }
   1596 
   1597 /*
   1599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1600 %                                                                             %
   1601 %                                                                             %
   1602 %                                                                             %
   1603 %   S e t G e o m e t r y I n f o                                             %
   1604 %                                                                             %
   1605 %                                                                             %
   1606 %                                                                             %
   1607 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1608 %
   1609 %  SetGeometryInfo sets the GeometryInfo structure to its default values.
   1610 %
   1611 %  The format of the SetGeometryInfo method is:
   1612 %
   1613 %      SetGeometryInfo(GeometryInfo *geometry_info)
   1614 %
   1615 %  A description of each parameter follows:
   1616 %
   1617 %    o geometry_info: the geometry info structure.
   1618 %
   1619 */
   1620 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
   1621 {
   1622   assert(geometry_info != (GeometryInfo *) NULL);
   1623   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
   1624   (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
   1625 }
   1626