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