]> granicus.if.org Git - imagemagick/blob - MagickCore/geometry.c
...
[imagemagick] / MagickCore / geometry.c
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-2017 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://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 \f
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 \f
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 */
96 MagickExport 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 \f
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 */
349 MagickExport 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 \f
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 */
514 MagickExport 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 \f
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 */
593 MagickExport 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 \f
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 */
634 MagickExport 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 \f
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 */
686 MagickExport 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 \f
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 */
727 MagickExport 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 \f
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 */
836 MagickExport 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 \f
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 */
1180 MagickExport 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 \f
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
1286 MagickExport 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         percent_flags;
1320
1321       PointInfo
1322         scale;
1323
1324       /*
1325         Geometry is a percentage of the image size.
1326       */
1327       percent_flags=ParseGeometry(geometry,&geometry_info);
1328       scale.x=geometry_info.rho;
1329       if ((percent_flags & RhoValue) == 0)
1330         scale.x=100.0;
1331       scale.y=geometry_info.sigma;
1332       if ((percent_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 \f
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 */
1463 MagickExport 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 \f
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 */
1532 MagickExport 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 \f
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 */
1571 MagickExport 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 \f
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 */
1605 MagickExport 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 }