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