• 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-2020 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("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     (void) GetNextToken(p,&p,MagickPathExtent,token);
768     if (*token == ',')
769       (void) 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       {
946         if (*(p+1) == ')')
947           return(flags);
948       }
949       case ')':
950       {
951         (void) CopyMagickString(p,p+1,MagickPathExtent);
952         break;
953       }
954       case 'x':
955       case 'X':
956       {
957         flags|=SeparatorValue;
958         p++;
959         break;
960       }
961       case '-':
962       case '+':
963       case ',':
964       case '0':
965       case '1':
966       case '2':
967       case '3':
968       case '4':
969       case '5':
970       case '6':
971       case '7':
972       case '8':
973       case '9':
974       case '/':
975       case 215:
976       case 'e':
977       case 'E':
978       {
979         p++;
980         break;
981       }
982       case '.':
983       {
984         p++;
985         flags|=DecimalValue;
986         break;
987       }
988       case ':':
989       {
990         p++;
991         flags|=AspectRatioValue;
992         break;
993       }
994       default:
995         return(NoValue);
996     }
997   }
998   /*
999     Parse rho, sigma, xi, psi, and optionally chi.
1000   */
1001   p=pedantic_geometry;
1002   if (*p == '\0')
1003     return(flags);
1004   q=p;
1005   value=StringToDouble(p,&q);
1006   if (LocaleNCompare(p,"0x",2) == 0)
1007     (void) strtol(p,&q,10);
1008   c=(int) ((unsigned char) *q);
1009   if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ':') ||
1010       (*q == ',') || (*q == '/') || (*q =='\0'))
1011     {
1012       /*
1013         Parse rho.
1014       */
1015       q=p;
1016       if (LocaleNCompare(p,"0x",2) == 0)
1017         value=(double) strtol(p,&p,10);
1018       else
1019         value=StringToDouble(p,&p);
1020       if (p != q)
1021         {
1022           flags|=RhoValue;
1023           geometry_info->rho=value;
1024         }
1025     }
1026   q=p;
1027   c=(int) ((unsigned char) *p);
1028   if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ':') || (*p == ',') ||
1029       (*p == '/'))
1030     {
1031       /*
1032         Parse sigma.
1033       */
1034       p++;
1035       while (isspace((int) ((unsigned char) *p)) != 0)
1036         p++;
1037       c=(int) ((unsigned char) *q);
1038       if (((c != 215) && (*q != 'x') && (*q != 'X') && (*q != ':')) ||
1039           ((*p != '+') && (*p != '-')))
1040         {
1041           q=p;
1042           value=StringToDouble(p,&p);
1043           if (p != q)
1044             {
1045               flags|=SigmaValue;
1046               geometry_info->sigma=value;
1047             }
1048         }
1049     }
1050   while (isspace((int) ((unsigned char) *p)) != 0)
1051     p++;
1052   if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1053     {
1054       /*
1055         Parse xi value.
1056       */
1057       if ((*p == ',') || (*p == '/') || (*p == ':') )
1058         p++;
1059       while ((*p == '+') || (*p == '-'))
1060       {
1061         if (*p == '-')
1062           flags^=XiNegative;  /* negate sign */
1063         p++;
1064       }
1065       q=p;
1066       value=StringToDouble(p,&p);
1067       if (p != q)
1068         {
1069           flags|=XiValue;
1070           if ((flags & XiNegative) != 0)
1071             value=(-value);
1072           geometry_info->xi=value;
1073         }
1074       while (isspace((int) ((unsigned char) *p)) != 0)
1075         p++;
1076       if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1077           (*p == ':'))
1078         {
1079           /*
1080             Parse psi value.
1081           */
1082           if ((*p == ',') || (*p == '/') || (*p == ':'))
1083             p++;
1084           while ((*p == '+') || (*p == '-'))
1085           {
1086             if (*p == '-')
1087               flags^=PsiNegative;  /* negate sign */
1088             p++;
1089           }
1090           q=p;
1091           value=StringToDouble(p,&p);
1092           if (p != q)
1093             {
1094               flags|=PsiValue;
1095               if ((flags & PsiNegative) != 0)
1096                 value=(-value);
1097               geometry_info->psi=value;
1098             }
1099       }
1100       while (isspace((int) ((unsigned char) *p)) != 0)
1101         p++;
1102       if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1103           (*p == ':'))
1104         {
1105           /*
1106             Parse chi value.
1107           */
1108           if ((*p == ',') || (*p == '/') || (*p == ':'))
1109             p++;
1110           while ((*p == '+') || (*p == '-'))
1111           {
1112             if (*p == '-')
1113               flags^=ChiNegative;  /* negate sign */
1114             p++;
1115           }
1116           q=p;
1117           value=StringToDouble(p,&p);
1118           if (p != q)
1119             {
1120               flags|=ChiValue;
1121               if ((flags & ChiNegative) != 0)
1122                 value=(-value);
1123               geometry_info->chi=value;
1124             }
1125         }
1126     }
1127   if (strchr(pedantic_geometry,':') != (char *) NULL)
1128     {
1129       /*
1130         Normalize sampling factor (e.g. 4:2:2 => 2x1).
1131       */
1132       if ((flags & SigmaValue) != 0)
1133         geometry_info->rho*=PerceptibleReciprocal(geometry_info->sigma);
1134       geometry_info->sigma=1.0;
1135       if (((flags & XiValue) != 0) && (geometry_info->xi == 0.0))
1136         geometry_info->sigma=2.0;
1137     }
1138   if (((flags & RhoValue) != 0) && ((flags & SigmaValue) == 0) &&
1139       ((flags & XiValue) != 0) && ((flags & XiNegative) != 0))
1140     {
1141       if ((flags & PsiValue) == 0)
1142         {
1143           /*
1144             Support negative height values (e.g. 30x-20).
1145           */
1146           geometry_info->sigma=geometry_info->xi;
1147           geometry_info->xi=0.0;
1148           flags|=SigmaValue;
1149           flags&=(~XiValue);
1150         }
1151       else
1152         if ((flags & ChiValue) == 0)
1153           {
1154             /*
1155               Support negative height values (e.g. 30x-20+10).
1156             */
1157             geometry_info->sigma=geometry_info->xi;
1158             geometry_info->xi=geometry_info->psi;
1159             flags|=SigmaValue;
1160             flags|=XiValue;
1161             flags&=(~PsiValue);
1162           }
1163         else
1164           {
1165             /*
1166               Support negative height values (e.g. 30x-20+10+10).
1167             */
1168             geometry_info->sigma=geometry_info->xi;
1169             geometry_info->xi=geometry_info->psi;
1170             geometry_info->psi=geometry_info->chi;
1171             flags|=SigmaValue;
1172             flags|=XiValue;
1173             flags|=PsiValue;
1174             flags&=(~ChiValue);
1175           }
1176     }
1177   if ((flags & PercentValue) != 0)
1178     {
1179       if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1180         geometry_info->sigma=geometry_info->rho;
1181       if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1182         geometry_info->rho=geometry_info->sigma;
1183     }
1184 #if 0
1185   /* Debugging Geometry */
1186   (void) fprintf(stderr,"ParseGeometry...\n");
1187   (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1188     (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1189     (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : "  ",
1190     (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : "  ",
1191     (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : "  ");
1192   (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1193     geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1194     geometry_info->chi);
1195 #endif
1196   return(flags);
1197 }
1198 
1199 /*
1200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201 %                                                                             %
1202 %                                                                             %
1203 %                                                                             %
1204 %   P a r s e G r a v i t y G e o m e t r y                                   %
1205 %                                                                             %
1206 %                                                                             %
1207 %                                                                             %
1208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209 %
1210 %  ParseGravityGeometry() returns a region as defined by the geometry string
1211 %  with respect to the given image page (canvas) dimensions and the images
1212 %  gravity setting.
1213 %
1214 %  This is typically used for specifing a area within a given image for
1215 %  cropping images to a smaller size, chopping out rows and or columns, or
1216 %  resizing and positioning overlay images.
1217 %
1218 %  Percentages are relative to image size and not page size, and are set to
1219 %  nearest integer (pixel) size.
1220 %
1221 %  The format of the ParseGravityGeometry method is:
1222 %
1223 %      MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1224 %        RectangeInfo *region_info,ExceptionInfo *exception)
1225 %
1226 %  A description of each parameter follows:
1227 %
1228 %    o geometry:  The geometry string (e.g. "100x100+10+10").
1229 %
1230 %    o region_info: the region as defined by the geometry string with respect
1231 %      to the image dimensions and its gravity.
1232 %
1233 %    o exception: return any errors or warnings in this structure.
1234 %
1235 */
ParseGravityGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1236 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1237   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1238 {
1239   MagickStatusType
1240     flags;
1241 
1242   size_t
1243     height,
1244     width;
1245 
1246   SetGeometry(image,region_info);
1247   if (image->page.width != 0)
1248     region_info->width=image->page.width;
1249   if (image->page.height != 0)
1250     region_info->height=image->page.height;
1251   flags=ParseAbsoluteGeometry(geometry,region_info);
1252   if (flags == NoValue)
1253     {
1254       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1255         "InvalidGeometry","`%s'",geometry);
1256       return(flags);
1257     }
1258   if ((flags & PercentValue) != 0)
1259     {
1260       GeometryInfo
1261         geometry_info;
1262 
1263       MagickStatusType
1264         status;
1265 
1266       PointInfo
1267         scale;
1268 
1269       /*
1270         Geometry is a percentage of the image size, not canvas size
1271       */
1272       if (image->gravity != UndefinedGravity)
1273         flags|=XValue | YValue;
1274       status=ParseGeometry(geometry,&geometry_info);
1275       scale.x=geometry_info.rho;
1276       if ((status & RhoValue) == 0)
1277         scale.x=100.0;
1278       scale.y=geometry_info.sigma;
1279       if ((status & SigmaValue) == 0)
1280         scale.y=scale.x;
1281       region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1282       region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
1283     }
1284   if ((flags & AspectRatioValue) != 0)
1285     {
1286       double
1287         geometry_ratio,
1288         image_ratio;
1289 
1290       GeometryInfo
1291         geometry_info;
1292 
1293       /*
1294         Geometry is a relative to image size and aspect ratio.
1295       */
1296       if (image->gravity != UndefinedGravity)
1297         flags|=XValue | YValue;
1298       (void) ParseGeometry(geometry,&geometry_info);
1299       geometry_ratio=geometry_info.rho;
1300       image_ratio=(double) image->columns/image->rows;
1301       if (geometry_ratio >= image_ratio)
1302         {
1303           region_info->width=image->columns;
1304           region_info->height=(size_t) floor((double) (image->rows*image_ratio/
1305             geometry_ratio)+0.5);
1306         }
1307       else
1308         {
1309           region_info->width=(size_t) floor((double) (image->columns*
1310             geometry_ratio/image_ratio)+0.5);
1311           region_info->height=image->rows;
1312         }
1313     }
1314   /*
1315     Adjust offset according to gravity setting.
1316   */
1317   width=region_info->width;
1318   height=region_info->height;
1319   if (width == 0)
1320     region_info->width=image->page.width | image->columns;
1321   if (height == 0)
1322     region_info->height=image->page.height | image->rows;
1323   GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1324   region_info->width=width;
1325   region_info->height=height;
1326   return(flags);
1327 }
1328 
1329 /*
1330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1331 %                                                                             %
1332 %                                                                             %
1333 %                                                                             %
1334 +   P a r s e M e t a G e o m e t r y                                         %
1335 %                                                                             %
1336 %                                                                             %
1337 %                                                                             %
1338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1339 %
1340 %  ParseMetaGeometry() is similar to GetGeometry() except the returned
1341 %  geometry is modified as determined by the meta characters:  %, !, <, >, @,
1342 %  :, and ^ in relation to image resizing.
1343 %
1344 %  Final image dimensions are adjusted so as to preserve the aspect ratio as
1345 %  much as possible, while generating a integer (pixel) size, and fitting the
1346 %  image within the specified geometry width and height.
1347 %
1348 %  Flags are interpreted...
1349 %     %   geometry size is given percentage of original width and height given
1350 %     !   do not try to preserve aspect ratio
1351 %     <   only enlarge images smaller that geometry
1352 %     >   only shrink images larger than geometry
1353 %     @   fit image to contain at most this many pixels
1354 %     :   width and height denotes an aspect ratio
1355 %     ^   contain the given geometry given, (minimal dimensions given)
1356 %
1357 %  The format of the ParseMetaGeometry method is:
1358 %
1359 %      MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1360 %        ssize_t *y, size_t *width,size_t *height)
1361 %
1362 %  A description of each parameter follows:
1363 %
1364 %    o geometry:  The geometry string (e.g. "100x100+10+10").
1365 %
1366 %    o x,y:  The x and y offset, set according to the geometry specification.
1367 %
1368 %    o width,height:  The width and height of original image, modified by
1369 %      the given geometry specification.
1370 %
1371 */
ParseMetaGeometry(const char * geometry,ssize_t * x,ssize_t * y,size_t * width,size_t * height)1372 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1373   ssize_t *y,size_t *width,size_t *height)
1374 {
1375   GeometryInfo
1376     geometry_info;
1377 
1378   MagickStatusType
1379     flags;
1380 
1381   size_t
1382     former_height,
1383     former_width;
1384 
1385   /*
1386     Ensure the image geometry is valid.
1387   */
1388   assert(x != (ssize_t *) NULL);
1389   assert(y != (ssize_t *) NULL);
1390   assert(width != (size_t *) NULL);
1391   assert(height != (size_t *) NULL);
1392   if ((geometry == (char *) NULL) || (*geometry == '\0'))
1393     return(NoValue);
1394   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1395   /*
1396     Parse geometry using GetGeometry.
1397   */
1398   SetGeometryInfo(&geometry_info);
1399   former_width=(*width);
1400   former_height=(*height);
1401   flags=GetGeometry(geometry,x,y,width,height);
1402   if ((flags & PercentValue) != 0)
1403     {
1404       MagickStatusType
1405         percent_flags;
1406 
1407       PointInfo
1408         scale;
1409 
1410       /*
1411         Geometry is a percentage of the image size.
1412       */
1413       percent_flags=ParseGeometry(geometry,&geometry_info);
1414       scale.x=geometry_info.rho;
1415       if ((percent_flags & RhoValue) == 0)
1416         scale.x=100.0;
1417       scale.y=geometry_info.sigma;
1418       if ((percent_flags & SigmaValue) == 0)
1419         scale.y=scale.x;
1420       *width=(size_t) MagickMax(floor(scale.x*former_width/100.0+0.5),1.0);
1421       *height=(size_t) MagickMax(floor(scale.y*former_height/100.0+0.5),1.0);
1422       former_width=(*width);
1423       former_height=(*height);
1424     }
1425   if ((flags & AspectRatioValue) != 0)
1426     {
1427       double
1428         geometry_ratio,
1429         image_ratio;
1430 
1431       /*
1432         Geometry is a relative to image size and aspect ratio.
1433       */
1434       (void) ParseGeometry(geometry,&geometry_info);
1435       geometry_ratio=geometry_info.rho;
1436       image_ratio=(double) former_width*
1437         PerceptibleReciprocal((double) former_height);
1438       if (geometry_ratio >= image_ratio)
1439         {
1440           *width=former_width;
1441           *height=(size_t) floor((double) (PerceptibleReciprocal(
1442             geometry_ratio)*former_height*image_ratio)+0.5);
1443         }
1444       else
1445         {
1446           *width=(size_t) floor((double) (PerceptibleReciprocal(
1447             image_ratio)*former_width*geometry_ratio)+0.5);
1448           *height=former_height;
1449         }
1450       former_width=(*width);
1451       former_height=(*height);
1452     }
1453   if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1454       (*height == former_height)))
1455     {
1456       if ((flags & RhoValue) == 0)
1457         *width=former_width;
1458       if ((flags & SigmaValue) == 0)
1459         *height=former_height;
1460     }
1461   else
1462     {
1463       double
1464         scale_factor;
1465 
1466       /*
1467         Respect aspect ratio of the image.
1468       */
1469       if ((former_width == 0) || (former_height == 0))
1470         scale_factor=1.0;
1471       else
1472         if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1473           {
1474             scale_factor=(double) *width/(double) former_width;
1475             if ((flags & MinimumValue) == 0)
1476               {
1477                 if (scale_factor > ((double) *height/(double) former_height))
1478                   scale_factor=(double) *height/(double) former_height;
1479               }
1480             else
1481               if (scale_factor < ((double) *height/(double) former_height))
1482                 scale_factor=(double) *height/(double) former_height;
1483           }
1484         else
1485           if ((flags & RhoValue) != 0)
1486             {
1487               scale_factor=(double) *width/(double) former_width;
1488               if (((flags & MinimumValue) != 0) &&
1489                   (scale_factor < ((double) *width/(double) former_height)))
1490                 scale_factor=(double) *width/(double) former_height;
1491             }
1492           else
1493             {
1494               scale_factor=(double) *height/(double) former_height;
1495               if (((flags & MinimumValue) != 0) &&
1496                   (scale_factor < ((double) *height/(double) former_width)))
1497                 scale_factor=(double) *height/(double) former_width;
1498             }
1499       *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1500       *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
1501     }
1502   if ((flags & GreaterValue) != 0)
1503     {
1504       if (former_width < *width)
1505         *width=former_width;
1506       if (former_height < *height)
1507         *height=former_height;
1508     }
1509   if ((flags & LessValue) != 0)
1510     {
1511       if (former_width > *width)
1512         *width=former_width;
1513       if (former_height > *height)
1514         *height=former_height;
1515     }
1516   if ((flags & AreaValue) != 0)
1517     {
1518       double
1519         area,
1520         distance;
1521 
1522       PointInfo
1523         scale;
1524 
1525       /*
1526         Geometry is a maximum area in pixels.
1527       */
1528       (void) ParseGeometry(geometry,&geometry_info);
1529       area=geometry_info.rho+sqrt(MagickEpsilon);
1530       distance=sqrt((double) former_width*former_height);
1531       scale.x=(double) former_width*PerceptibleReciprocal(distance/sqrt(area));
1532       scale.y=(double) former_height*PerceptibleReciprocal(distance/sqrt(area));
1533       if ((scale.x < (double) *width) || (scale.y < (double) *height))
1534         {
1535           *width=(unsigned long) (former_width*PerceptibleReciprocal(
1536             distance/sqrt(area)));
1537           *height=(unsigned long) (former_height*PerceptibleReciprocal(
1538             distance/sqrt(area)));
1539         }
1540       former_width=(*width);
1541       former_height=(*height);
1542     }
1543   return(flags);
1544 }
1545 
1546 /*
1547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548 %                                                                             %
1549 %                                                                             %
1550 %                                                                             %
1551 %   P a r s e P a g e G e o m e t r y                                         %
1552 %                                                                             %
1553 %                                                                             %
1554 %                                                                             %
1555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1556 %
1557 %  ParsePageGeometry() returns a region as defined by the geometry string with
1558 %  respect to the image page (canvas) dimensions.
1559 %
1560 %  WARNING: Percentage dimensions remain relative to the actual image
1561 %  dimensions, and not canvas dimensions.
1562 %
1563 %  The format of the ParsePageGeometry method is:
1564 %
1565 %      MagickStatusType ParsePageGeometry(const Image *image,
1566 %        const char *geometry,RectangeInfo *region_info,
1567 %        ExceptionInfo *exception)
1568 %
1569 %  A description of each parameter follows:
1570 %
1571 %    o geometry:  The geometry string (e.g. "100x100+10+10").
1572 %
1573 %    o region_info: the region as defined by the geometry string with
1574 %      respect to the image and its gravity.
1575 %
1576 %    o exception: return any errors or warnings in this structure.
1577 %
1578 */
ParsePageGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1579 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1580   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1581 {
1582   MagickStatusType
1583     flags;
1584 
1585   SetGeometry(image,region_info);
1586   if (image->page.width != 0)
1587     region_info->width=image->page.width;
1588   if (image->page.height != 0)
1589     region_info->height=image->page.height;
1590   flags=ParseAbsoluteGeometry(geometry,region_info);
1591   if (flags == NoValue)
1592     {
1593       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1594         "InvalidGeometry","`%s'",geometry);
1595       return(flags);
1596     }
1597   if ((flags & PercentValue) != 0)
1598     {
1599       region_info->width=image->columns;
1600       region_info->height=image->rows;
1601     }
1602   flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1603     &region_info->width,&region_info->height);
1604   if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1605       (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1606     {
1607       if ((flags & WidthValue) == 0)
1608         region_info->width=region_info->height;
1609       if ((flags & HeightValue) == 0)
1610         region_info->height=region_info->width;
1611     }
1612   return(flags);
1613 }
1614 
1615 /*
1616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617 %                                                                             %
1618 %                                                                             %
1619 %                                                                             %
1620 %   P a r s e R e g i o n G e o m e t r y                                     %
1621 %                                                                             %
1622 %                                                                             %
1623 %                                                                             %
1624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 %
1626 %  ParseRegionGeometry() returns a region as defined by the geometry string
1627 %  with respect to the image dimensions and aspect ratio.
1628 %
1629 %  This is basically a wrapper around ParseMetaGeometry.  This is typically
1630 %  used to parse a geometry string to work out the final integer dimensions
1631 %  for image resizing.
1632 %
1633 %  The format of the ParseRegionGeometry method is:
1634 %
1635 %      MagickStatusType ParseRegionGeometry(const Image *image,
1636 %        const char *geometry,RectangeInfo *region_info,
1637 %        ExceptionInfo *exception)
1638 %
1639 %  A description of each parameter follows:
1640 %
1641 %    o geometry:  The geometry string (e.g. "100x100+10+10").
1642 %
1643 %    o region_info: the region as defined by the geometry string.
1644 %
1645 %    o exception: return any errors or warnings in this structure.
1646 %
1647 */
ParseRegionGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1648 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1649   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1650 {
1651   MagickStatusType
1652     flags;
1653 
1654   SetGeometry(image,region_info);
1655   flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1656     &region_info->width,&region_info->height);
1657   if (flags == NoValue)
1658     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1659       "InvalidGeometry","`%s'",geometry);
1660   return(flags);
1661 }
1662 
1663 /*
1664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1665 %                                                                             %
1666 %                                                                             %
1667 %                                                                             %
1668 %   S e t G e o m e t r y                                                     %
1669 %                                                                             %
1670 %                                                                             %
1671 %                                                                             %
1672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1673 %
1674 %  SetGeometry() sets the geometry to its default values.
1675 %
1676 %  The format of the SetGeometry method is:
1677 %
1678 %      SetGeometry(const Image *image,RectangleInfo *geometry)
1679 %
1680 %  A description of each parameter follows:
1681 %
1682 %    o image: the image.
1683 %
1684 %    o geometry: the geometry.
1685 %
1686 */
SetGeometry(const Image * image,RectangleInfo * geometry)1687 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1688 {
1689   assert(image != (Image *) NULL);
1690   assert(image->signature == MagickCoreSignature);
1691   if (image->debug != MagickFalse)
1692     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1693   assert(geometry != (RectangleInfo *) NULL);
1694   (void) memset(geometry,0,sizeof(*geometry));
1695   geometry->width=image->columns;
1696   geometry->height=image->rows;
1697 }
1698 
1699 /*
1700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1701 %                                                                             %
1702 %                                                                             %
1703 %                                                                             %
1704 %   S e t G e o m e t r y I n f o                                             %
1705 %                                                                             %
1706 %                                                                             %
1707 %                                                                             %
1708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1709 %
1710 %  SetGeometryInfo sets the GeometryInfo structure to its default values.
1711 %
1712 %  The format of the SetGeometryInfo method is:
1713 %
1714 %      SetGeometryInfo(GeometryInfo *geometry_info)
1715 %
1716 %  A description of each parameter follows:
1717 %
1718 %    o geometry_info: the geometry info structure.
1719 %
1720 */
SetGeometryInfo(GeometryInfo * geometry_info)1721 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1722 {
1723   assert(geometry_info != (GeometryInfo *) NULL);
1724   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1725   (void) memset(geometry_info,0,sizeof(*geometry_info));
1726 }
1727