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