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