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