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