MagickCore  7.1.1-43
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/script/license.php %
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=CastDoubleToUnsigned(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=CastDoubleToUnsigned(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=CastDoubleToLong(StringToDouble(p,&p));
394  if (p != q)
395  {
396  flags|=XValue;
397  if (((flags & XNegative) != 0) && (x != (ssize_t *) NULL))
398  *x=CastDoubleToLong(-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=CastDoubleToLong(StringToDouble(p,&p));
415  if (p != q)
416  {
417  flags|=YValue;
418  if (((flags & YNegative) != 0) && (y != (ssize_t *) NULL))
419  *y=CastDoubleToLong(-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=CastDoubleToLong((double) width-region->width-region->x);
561  break;
562  }
563  case NorthGravity:
564  case SouthGravity:
565  case CenterGravity:
566  {
567  region->x=CastDoubleToLong(width/2.0-region->width/2.0+region->x);
568  break;
569  }
570  case ForgetGravity:
571  case NorthWestGravity:
572  case WestGravity:
573  case SouthWestGravity:
574  default:
575  break;
576  }
577  switch (gravity)
578  {
579  case SouthWestGravity:
580  case SouthGravity:
581  case SouthEastGravity:
582  {
583  region->y=CastDoubleToLong((double) height-region->height-region->y);
584  break;
585  }
586  case EastGravity:
587  case WestGravity:
588  case CenterGravity:
589  {
590  region->y=CastDoubleToLong(height/2.0-region->height/2.0+region->y);
591  break;
592  }
593  case ForgetGravity:
594  case NorthWestGravity:
595  case NorthGravity:
596  case NorthEastGravity:
597  default:
598  break;
599  }
600  return;
601 }
602 
603 /*
604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
605 % %
606 % %
607 % %
608 + I s G e o m e t r y %
609 % %
610 % %
611 % %
612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
613 %
614 % IsGeometry() returns MagickTrue if the geometry specification is valid.
615 % Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
616 %
617 % The format of the IsGeometry method is:
618 %
619 % MagickBooleanType IsGeometry(const char *geometry)
620 %
621 % A description of each parameter follows:
622 %
623 % o geometry: This string is the geometry specification.
624 %
625 */
626 MagickExport MagickBooleanType IsGeometry(const char *geometry)
627 {
629  geometry_info;
630 
631  MagickStatusType
632  flags;
633 
634  if (geometry == (const char *) NULL)
635  return(MagickFalse);
636  flags=ParseGeometry(geometry,&geometry_info);
637  return(flags != NoValue ? MagickTrue : MagickFalse);
638 }
639 
640 /*
641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642 % %
643 % %
644 % %
645 + I s S c e n e G e o m e t r y %
646 % %
647 % %
648 % %
649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650 %
651 % IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
652 % specification (e.g. [1], [1-9], [1,7,4]).
653 %
654 % The format of the IsSceneGeometry method is:
655 %
656 % MagickBooleanType IsSceneGeometry(const char *geometry,
657 % const MagickBooleanType pedantic)
658 %
659 % A description of each parameter follows:
660 %
661 % o geometry: This string is the geometry specification.
662 %
663 % o pedantic: A value other than 0 invokes a more restrictive set of
664 % conditions for a valid specification (e.g. [1], [1-4], [4-1]).
665 %
666 */
667 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
668  const MagickBooleanType pedantic)
669 {
670  char
671  *p;
672 
673  double
674  value;
675 
676  if (geometry == (const char *) NULL)
677  return(MagickFalse);
678  p=(char *) geometry;
679  value=StringToDouble(geometry,&p);
680  if (IsNaN(value) != 0)
681  return(MagickFalse);
682  if (value > (double) MAGICK_SSIZE_MAX)
683  return(MagickFalse);
684  if (value < (double) MAGICK_SSIZE_MIN)
685  return(MagickFalse);
686  if (p == geometry)
687  return(MagickFalse);
688  if (strspn(geometry,"0123456789-, ") != strlen(geometry))
689  return(MagickFalse);
690  if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
691  return(MagickFalse);
692  return(MagickTrue);
693 }
694 
695 /*
696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
697 % %
698 % %
699 % %
700 + L i s t P a g e s i z e s %
701 % %
702 % %
703 % %
704 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
705 %
706 % ListPagesizes() lists the pagesizes and their associated geometry.
707 %
708 % The format of the ListPagesizes method is:
709 %
710 % MagickBooleanType ListPagesizes(FILE *file,ExceptionInfo *exception)
711 %
712 % A description of each parameter follows.
713 %
714 % o file: An pointer to the output FILE.
715 %
716 % o exception: return any errors or warnings in this structure.
717 %
718 */
719 MagickExport MagickBooleanType ListPagesizes(FILE *file,
720  ExceptionInfo *magick_unused(exception))
721 {
722 #define MaxMagickSpaces ((int) sizeof(Pagesizes[0].name))
723 
724  const char
725  *spacer = " ";
726 
727  ssize_t
728  i;
729 
730  magick_unreferenced(exception);
731  if (file == (FILE *) NULL)
732  file=stdout;
733  (void) FormatLocaleFile(file,"\nPagesize Geometry \n");
734  (void) FormatLocaleFile(file,"---------------------\n");
735  for (i=0; *Pagesizes[i].name != '\0'; i++)
736  (void) FormatLocaleFile(file,"%s%.*s%s\n",Pagesizes[i].name,
737  MaxMagickSpaces-(int) Pagesizes[i].extent,spacer,Pagesizes[i].geometry);
738  return(MagickTrue);
739 }
740 
741 /*
742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
743 % %
744 % %
745 % %
746 % P a r s e A b s o l u t e G e o m e t r y %
747 % %
748 % %
749 % %
750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751 %
752 % ParseAbsoluteGeometry() returns a region as defined by the geometry string,
753 % without any modification by percentages or gravity.
754 %
755 % It currently just a wrapper around GetGeometry(), but may be expanded in
756 % the future to handle other positioning information.
757 %
758 % The format of the ParseAbsoluteGeometry method is:
759 %
760 % MagickStatusType ParseAbsoluteGeometry(const char *geometry,
761 % RectangleInfo *region_info)
762 %
763 % A description of each parameter follows:
764 %
765 % o geometry: The geometry string (e.g. "100x100+10+10").
766 %
767 % o region_info: the region as defined by the geometry string.
768 %
769 */
770 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
771  RectangleInfo *region_info)
772 {
773  MagickStatusType
774  flags;
775 
776  flags=GetGeometry(geometry,&region_info->x,&region_info->y,
777  &region_info->width,&region_info->height);
778  return(flags);
779 }
780 
781 /*
782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783 % %
784 % %
785 % %
786 % P a r s e A f f i n e G e o m e t r y %
787 % %
788 % %
789 % %
790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791 %
792 % ParseAffineGeometry() returns an affine matrix as defined by a string of 4
793 % to 6 comma/space separated floating point values.
794 %
795 % The affine matrix determinant is checked for validity of the values.
796 %
797 % The format of the ParseAffineGeometry method is:
798 %
799 % MagickStatusType ParseAffineGeometry(const char *geometry,
800 % AffineMatrix *affine_matrix,ExceptionInfo *exception)
801 %
802 % A description of each parameter follows:
803 %
804 % o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
805 %
806 % o affine_matrix: the affine matrix as defined by the geometry string.
807 %
808 % o exception: return any errors or warnings in this structure.
809 %
810 */
811 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
812  AffineMatrix *affine_matrix,ExceptionInfo *exception)
813 {
814  char
815  token[MagickPathExtent];
816 
817  const char
818  *p;
819 
820  double
821  determinant;
822 
823  MagickStatusType
824  flags;
825 
826  ssize_t
827  i;
828 
829  GetAffineMatrix(affine_matrix);
830  flags=NoValue;
831  p=(char *) geometry;
832  for (i=0; (*p != '\0') && (i < 6); i++)
833  {
834  (void) GetNextToken(p,&p,MagickPathExtent,token);
835  if (*token == ',')
836  (void) GetNextToken(p,&p,MagickPathExtent,token);
837  switch (i)
838  {
839  case 0:
840  {
841  affine_matrix->sx=StringToDouble(token,(char **) NULL);
842  break;
843  }
844  case 1:
845  {
846  affine_matrix->rx=StringToDouble(token,(char **) NULL);
847  break;
848  }
849  case 2:
850  {
851  affine_matrix->ry=StringToDouble(token,(char **) NULL);
852  break;
853  }
854  case 3:
855  {
856  affine_matrix->sy=StringToDouble(token,(char **) NULL);
857  break;
858  }
859  case 4:
860  {
861  affine_matrix->tx=StringToDouble(token,(char **) NULL);
862  flags|=XValue;
863  break;
864  }
865  case 5:
866  {
867  affine_matrix->ty=StringToDouble(token,(char **) NULL);
868  flags|=YValue;
869  break;
870  }
871  }
872  }
873  determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
874  affine_matrix->ry);
875  if (fabs(determinant) < MagickEpsilon)
876  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
877  "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
878  return(flags);
879 }
880 
881 /*
882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
883 % %
884 % %
885 % %
886 % P a r s e G e o m e t r y %
887 % %
888 % %
889 % %
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891 %
892 % ParseGeometry() parses a geometry specification and returns the sigma,
893 % rho, xi, and psi values. It also returns flags that indicates which
894 % of the four values (sigma, rho, xi, psi) were located in the string, and
895 % whether the xi or pi values are negative.
896 %
897 % In addition, it reports if there are any of meta characters (%, !, <, >, @,
898 % and ^) flags present. It does not report the location of the percentage
899 % relative to the values.
900 %
901 % Values may also be separated by commas, colons, or slashes, and offsets.
902 % Chroma subsampling definitions have to be in the form of a:b:c. Offsets may
903 % be prefixed by multiple signs to make offset string substitutions easier to
904 % handle from shell scripts. For example: "-10-10", "-+10-+10", or "+-10+-10"
905 % will generate negative offsets, while "+10+10", "++10++10", or "--10--10"
906 % will generate positive offsets.
907 %
908 % The format of the ParseGeometry method is:
909 %
910 % MagickStatusType ParseGeometry(const char *geometry,
911 % GeometryInfo *geometry_info)
912 %
913 % A description of each parameter follows:
914 %
915 % o geometry: The geometry string (e.g. "100x100+10+10").
916 %
917 % o geometry_info: returns the parsed width/height/x/y in this structure.
918 %
919 */
920 MagickExport MagickStatusType ParseGeometry(const char *geometry,
921  GeometryInfo *geometry_info)
922 {
923  char
924  *p,
925  pedantic_geometry[MagickPathExtent],
926  *q;
927 
928  double
929  value;
930 
932  coordinate;
933 
934  int
935  c;
936 
937  MagickStatusType
938  flags;
939 
940  /*
941  Remove whitespaces meta characters from geometry specification.
942  */
943  assert(geometry_info != (GeometryInfo *) NULL);
944  (void) memset(geometry_info,0,sizeof(*geometry_info));
945  flags=NoValue;
946  if ((geometry == (char *) NULL) || (*geometry == '\0'))
947  return(flags);
948  if (strlen(geometry) >= (MagickPathExtent-1))
949  return(flags);
950  c=sscanf(geometry,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",&coordinate.rho,
951  &coordinate.sigma,&coordinate.xi,&coordinate.psi);
952  if (c == 4)
953  {
954  /*
955  Special case: coordinate (e.g. 0,0 255,255).
956  */
957  geometry_info->rho=coordinate.rho;
958  geometry_info->sigma=coordinate.sigma;
959  geometry_info->xi=coordinate.xi;
960  geometry_info->psi=coordinate.psi;
961  flags|=RhoValue | SigmaValue | XiValue | PsiValue;
962  return(flags);
963  }
964  (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
965  for (p=pedantic_geometry; *p != '\0'; )
966  {
967  c=(int) ((unsigned char) *p);
968  if (isspace((int) ((unsigned char) c)) != 0)
969  {
970  (void) CopyMagickString(p,p+1,MagickPathExtent);
971  continue;
972  }
973  switch (c)
974  {
975  case '%':
976  {
977  flags|=PercentValue;
978  (void) CopyMagickString(p,p+1,MagickPathExtent);
979  break;
980  }
981  case '!':
982  {
983  flags|=AspectValue;
984  (void) CopyMagickString(p,p+1,MagickPathExtent);
985  break;
986  }
987  case '<':
988  {
989  flags|=LessValue;
990  (void) CopyMagickString(p,p+1,MagickPathExtent);
991  break;
992  }
993  case '>':
994  {
995  flags|=GreaterValue;
996  (void) CopyMagickString(p,p+1,MagickPathExtent);
997  break;
998  }
999  case '#':
1000  {
1001  flags|=MaximumValue;
1002  (void) CopyMagickString(p,p+1,MagickPathExtent);
1003  break;
1004  }
1005  case '^':
1006  {
1007  flags|=MinimumValue;
1008  (void) CopyMagickString(p,p+1,MagickPathExtent);
1009  break;
1010  }
1011  case '@':
1012  {
1013  flags|=AreaValue;
1014  (void) CopyMagickString(p,p+1,MagickPathExtent);
1015  break;
1016  }
1017  case '(':
1018  {
1019  if (*(p+1) == ')')
1020  return(flags);
1021  (void) CopyMagickString(p,p+1,MagickPathExtent);
1022  break;
1023  }
1024  case ')':
1025  {
1026  (void) CopyMagickString(p,p+1,MagickPathExtent);
1027  break;
1028  }
1029  case 'x':
1030  case 'X':
1031  {
1032  flags|=SeparatorValue;
1033  p++;
1034  break;
1035  }
1036  case '-':
1037  case '+':
1038  case ',':
1039  case '0':
1040  case '1':
1041  case '2':
1042  case '3':
1043  case '4':
1044  case '5':
1045  case '6':
1046  case '7':
1047  case '8':
1048  case '9':
1049  case '/':
1050  case 215:
1051  case 'e':
1052  case 'E':
1053  {
1054  p++;
1055  break;
1056  }
1057  case '.':
1058  {
1059  p++;
1060  flags|=DecimalValue;
1061  break;
1062  }
1063  case ':':
1064  {
1065  p++;
1066  flags|=AspectRatioValue;
1067  break;
1068  }
1069  default:
1070  return(NoValue);
1071  }
1072  }
1073  /*
1074  Parse rho, sigma, xi, psi, and optionally chi.
1075  */
1076  p=pedantic_geometry;
1077  if (*p == '\0')
1078  return(flags);
1079  q=p;
1080  value=StringToDouble(p,&q);
1081  if (LocaleNCompare(p,"0x",2) == 0)
1082  (void) strtol(p,&q,10);
1083  c=(int) ((unsigned char) *q);
1084  if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ':') ||
1085  (*q == ',') || (*q == '/') || (*q =='\0'))
1086  {
1087  /*
1088  Parse rho.
1089  */
1090  q=p;
1091  if (LocaleNCompare(p,"0x",2) == 0)
1092  value=(double) strtol(p,&p,10);
1093  else
1094  value=StringToDouble(p,&p);
1095  if (p != q)
1096  {
1097  flags|=RhoValue;
1098  geometry_info->rho=value;
1099  }
1100  }
1101  q=p;
1102  c=(int) ((unsigned char) *p);
1103  if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ':') || (*p == ',') ||
1104  (*p == '/'))
1105  {
1106  /*
1107  Parse sigma.
1108  */
1109  p++;
1110  while (isspace((int) ((unsigned char) *p)) != 0)
1111  p++;
1112  c=(int) ((unsigned char) *q);
1113  if (((c != 215) && (*q != 'x') && (*q != 'X') && (*q != ':')) ||
1114  ((*p != '+') && (*p != '-')))
1115  {
1116  q=p;
1117  value=StringToDouble(p,&p);
1118  if (p != q)
1119  {
1120  flags|=SigmaValue;
1121  geometry_info->sigma=value;
1122  }
1123  }
1124  }
1125  while (isspace((int) ((unsigned char) *p)) != 0)
1126  p++;
1127  if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1128  {
1129  /*
1130  Parse xi value.
1131  */
1132  if ((*p == ',') || (*p == '/') || (*p == ':') )
1133  p++;
1134  while ((*p == '+') || (*p == '-'))
1135  {
1136  if (*p == '-')
1137  flags^=XiNegative; /* negate sign */
1138  p++;
1139  }
1140  q=p;
1141  value=StringToDouble(p,&p);
1142  if (p != q)
1143  {
1144  flags|=XiValue;
1145  if ((flags & XiNegative) != 0)
1146  value=(-value);
1147  geometry_info->xi=value;
1148  }
1149  while (isspace((int) ((unsigned char) *p)) != 0)
1150  p++;
1151  if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1152  (*p == ':'))
1153  {
1154  /*
1155  Parse psi value.
1156  */
1157  if ((*p == ',') || (*p == '/') || (*p == ':'))
1158  p++;
1159  while ((*p == '+') || (*p == '-'))
1160  {
1161  if (*p == '-')
1162  flags^=PsiNegative; /* negate sign */
1163  p++;
1164  }
1165  q=p;
1166  value=StringToDouble(p,&p);
1167  if (p != q)
1168  {
1169  flags|=PsiValue;
1170  if ((flags & PsiNegative) != 0)
1171  value=(-value);
1172  geometry_info->psi=value;
1173  }
1174  }
1175  while (isspace((int) ((unsigned char) *p)) != 0)
1176  p++;
1177  if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1178  (*p == ':'))
1179  {
1180  /*
1181  Parse chi value.
1182  */
1183  if ((*p == ',') || (*p == '/') || (*p == ':'))
1184  p++;
1185  while ((*p == '+') || (*p == '-'))
1186  {
1187  if (*p == '-')
1188  flags^=ChiNegative; /* negate sign */
1189  p++;
1190  }
1191  q=p;
1192  value=StringToDouble(p,&p);
1193  if (p != q)
1194  {
1195  flags|=ChiValue;
1196  if ((flags & ChiNegative) != 0)
1197  value=(-value);
1198  geometry_info->chi=value;
1199  }
1200  }
1201  }
1202  if (strchr(pedantic_geometry,':') != (char *) NULL)
1203  {
1204  /*
1205  Normalize sampling factor (e.g. 4:2:2 => 2x1).
1206  */
1207  if ((flags & SigmaValue) != 0)
1208  geometry_info->rho*=PerceptibleReciprocal(geometry_info->sigma);
1209  geometry_info->sigma=1.0;
1210  if (((flags & XiValue) != 0) && (geometry_info->xi == 0.0))
1211  geometry_info->sigma=2.0;
1212  }
1213  if (((flags & RhoValue) != 0) && ((flags & SigmaValue) == 0) &&
1214  ((flags & XiValue) != 0) && ((flags & XiNegative) != 0))
1215  {
1216  if ((flags & PsiValue) == 0)
1217  {
1218  /*
1219  Support negative height values (e.g. 30x-20).
1220  */
1221  geometry_info->sigma=geometry_info->xi;
1222  geometry_info->xi=0.0;
1223  flags|=SigmaValue;
1224  flags&=(unsigned int) (~XiValue);
1225  }
1226  else
1227  if ((flags & ChiValue) == 0)
1228  {
1229  /*
1230  Support negative height values (e.g. 30x-20+10).
1231  */
1232  geometry_info->sigma=geometry_info->xi;
1233  geometry_info->xi=geometry_info->psi;
1234  flags|=SigmaValue;
1235  flags|=XiValue;
1236  flags&=(unsigned int) (~PsiValue);
1237  }
1238  else
1239  {
1240  /*
1241  Support negative height values (e.g. 30x-20+10+10).
1242  */
1243  geometry_info->sigma=geometry_info->xi;
1244  geometry_info->xi=geometry_info->psi;
1245  geometry_info->psi=geometry_info->chi;
1246  flags|=SigmaValue;
1247  flags|=XiValue;
1248  flags|=PsiValue;
1249  flags&=(unsigned int) (~ChiValue);
1250  }
1251  }
1252  if ((flags & PercentValue) != 0)
1253  {
1254  if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1255  geometry_info->sigma=geometry_info->rho;
1256  if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1257  geometry_info->rho=geometry_info->sigma;
1258  }
1259 #if 0
1260  /* Debugging Geometry */
1261  (void) fprintf(stderr,"ParseGeometry...\n");
1262  (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1263  (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1264  (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1265  (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1266  (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1267  (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1268  geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1269  geometry_info->chi);
1270 #endif
1271  return(flags);
1272 }
1273 
1274 /*
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276 % %
1277 % %
1278 % %
1279 % P a r s e G r a v i t y G e o m e t r y %
1280 % %
1281 % %
1282 % %
1283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284 %
1285 % ParseGravityGeometry() returns a region as defined by the geometry string
1286 % with respect to the given image page (canvas) dimensions and the images
1287 % gravity setting.
1288 %
1289 % This is typically used for specifying a area within a given image for
1290 % cropping images to a smaller size, chopping out rows and or columns, or
1291 % resizing and positioning overlay images.
1292 %
1293 % Percentages are relative to image size and not page size, and are set to
1294 % nearest integer (pixel) size.
1295 %
1296 % The format of the ParseGravityGeometry method is:
1297 %
1298 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1299 % RectangleInfo *region_info,ExceptionInfo *exception)
1300 %
1301 % A description of each parameter follows:
1302 %
1303 % o geometry: The geometry string (e.g. "100x100+10+10").
1304 %
1305 % o region_info: the region as defined by the geometry string with respect
1306 % to the image dimensions and its gravity.
1307 %
1308 % o exception: return any errors or warnings in this structure.
1309 %
1310 */
1311 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1312  const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1313 {
1314  MagickStatusType
1315  flags;
1316 
1317  size_t
1318  height,
1319  width;
1320 
1321  if (IsEventLogging() != MagickFalse)
1322  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1323  if ((geometry == (char *) NULL) || (*geometry == '\0'))
1324  return(NoValue);
1325  SetGeometry(image,region_info);
1326  if (image->page.width != 0)
1327  region_info->width=image->page.width;
1328  if (image->page.height != 0)
1329  region_info->height=image->page.height;
1330  flags=ParseAbsoluteGeometry(geometry,region_info);
1331  if (flags == NoValue)
1332  {
1333  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1334  "InvalidGeometry","`%s'",geometry);
1335  return(flags);
1336  }
1337  if ((flags & PercentValue) != 0)
1338  {
1339  GeometryInfo
1340  geometry_info;
1341 
1342  MagickStatusType
1343  status;
1344 
1345  PointInfo
1346  scale;
1347 
1348  /*
1349  Geometry is a percentage of the image size, not canvas size
1350  */
1351  if (image->gravity != UndefinedGravity)
1352  flags|=XValue | YValue;
1353  status=ParseGeometry(geometry,&geometry_info);
1354  scale.x=geometry_info.rho;
1355  if ((status & RhoValue) == 0)
1356  scale.x=100.0;
1357  scale.y=geometry_info.sigma;
1358  if ((status & SigmaValue) == 0)
1359  scale.y=scale.x;
1360  region_info->width=CastDoubleToUnsigned(scale.x*image->columns/100.0+0.5);
1361  region_info->height=CastDoubleToUnsigned(scale.y*image->rows/100.0+0.5);
1362  }
1363  if ((flags & AspectRatioValue) != 0)
1364  {
1365  double
1366  geometry_ratio,
1367  image_ratio;
1368 
1369  GeometryInfo
1370  geometry_info;
1371 
1372  /*
1373  Geometry is a relative to image size and aspect ratio.
1374  */
1375  if (image->gravity != UndefinedGravity)
1376  flags|=XValue | YValue;
1377  (void) ParseGeometry(geometry,&geometry_info);
1378  geometry_ratio=geometry_info.rho;
1379  image_ratio=(double) image->columns/image->rows;
1380  region_info->width=image->columns;
1381  region_info->height=image->rows;
1382  if ((flags & MaximumValue) != 0)
1383  {
1384  if (geometry_ratio < image_ratio)
1385  region_info->height=CastDoubleToUnsigned((double) image->rows*
1386  image_ratio/geometry_ratio+0.5);
1387  else
1388  region_info->width=CastDoubleToUnsigned((double) image->columns*
1389  geometry_ratio/image_ratio+0.5);
1390  }
1391  else
1392  if (geometry_ratio >= image_ratio)
1393  region_info->height=CastDoubleToUnsigned((double) image->rows*
1394  image_ratio/geometry_ratio+0.5);
1395  else
1396  region_info->width=CastDoubleToUnsigned((double) image->columns*
1397  geometry_ratio/image_ratio+0.5);
1398  }
1399  /*
1400  Adjust offset according to gravity setting.
1401  */
1402  width=region_info->width;
1403  height=region_info->height;
1404  if (width == 0)
1405  region_info->width=image->page.width | image->columns;
1406  if (height == 0)
1407  region_info->height=image->page.height | image->rows;
1408  GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1409  if ((flags & LessValue) != 0)
1410  if ((region_info->width < image->columns) &&
1411  (region_info->height < image->rows))
1412  {
1413  SetGeometry(image,region_info);
1414  return(NoValue);
1415  }
1416  if ((flags & GreaterValue) != 0)
1417  if ((region_info->width > image->columns) &&
1418  (region_info->height > image->rows))
1419  {
1420  SetGeometry(image,region_info);
1421  return(NoValue);
1422  }
1423  region_info->width=width;
1424  region_info->height=height;
1425  return(flags);
1426 }
1427 
1428 /*
1429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430 % %
1431 % %
1432 % %
1433 + P a r s e M e t a G e o m e t r y %
1434 % %
1435 % %
1436 % %
1437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438 %
1439 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1440 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1441 % :, and ^ in relation to image resizing.
1442 %
1443 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1444 % much as possible, while generating a integer (pixel) size, and fitting the
1445 % image within the specified geometry width and height.
1446 %
1447 % Flags are interpreted...
1448 % % geometry size is given percentage of original width and height given
1449 % ! do not try to preserve aspect ratio
1450 % < only enlarge images smaller that geometry
1451 % > only shrink images larger than geometry
1452 % @ fit image to contain at most this many pixels
1453 % : width and height denotes an aspect ratio
1454 % ^ contain the given geometry given, (minimal dimensions given)
1455 %
1456 % The format of the ParseMetaGeometry method is:
1457 %
1458 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1459 % ssize_t *y, size_t *width,size_t *height)
1460 %
1461 % A description of each parameter follows:
1462 %
1463 % o geometry: The geometry string (e.g. "100x100+10+10").
1464 %
1465 % o x,y: The x and y offset, set according to the geometry specification.
1466 %
1467 % o width,height: The width and height of original image, modified by
1468 % the given geometry specification.
1469 %
1470 */
1471 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1472  ssize_t *y,size_t *width,size_t *height)
1473 {
1474  GeometryInfo
1475  geometry_info;
1476 
1477  MagickStatusType
1478  flags;
1479 
1480  size_t
1481  stasis_height,
1482  stasis_width;
1483 
1484  /*
1485  Ensure the image geometry is valid.
1486  */
1487  assert(x != (ssize_t *) NULL);
1488  assert(y != (ssize_t *) NULL);
1489  assert(width != (size_t *) NULL);
1490  assert(height != (size_t *) NULL);
1491  if (IsEventLogging() != MagickFalse)
1492  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1493  if ((geometry == (char *) NULL) || (*geometry == '\0'))
1494  return(NoValue);
1495  /*
1496  Parse geometry using GetGeometry.
1497  */
1498  stasis_width=(*width);
1499  stasis_height=(*height);
1500  SetGeometryInfo(&geometry_info);
1501  flags=GetGeometry(geometry,x,y,width,height);
1502  if ((flags & PercentValue) != 0)
1503  {
1504  MagickStatusType
1505  percent_flags;
1506 
1507  PointInfo
1508  scale;
1509 
1510  /*
1511  Geometry is a percentage of the image size.
1512  */
1513  percent_flags=ParseGeometry(geometry,&geometry_info);
1514  scale.x=geometry_info.rho;
1515  if ((percent_flags & RhoValue) == 0)
1516  scale.x=100.0;
1517  scale.y=geometry_info.sigma;
1518  if ((percent_flags & SigmaValue) == 0)
1519  scale.y=scale.x;
1520  *width=CastDoubleToUnsigned(scale.x*stasis_width/100.0+0.5);
1521  *height=CastDoubleToUnsigned(scale.y*stasis_height/100.0+0.5);
1522  stasis_width=(*width);
1523  stasis_height=(*height);
1524  }
1525  if ((flags & AspectRatioValue) != 0)
1526  {
1527  double
1528  geometry_ratio,
1529  image_ratio;
1530 
1531  /*
1532  Geometry is a relative to image size and aspect ratio.
1533  */
1534  (void) ParseGeometry(geometry,&geometry_info);
1535  geometry_ratio=geometry_info.rho;
1536  image_ratio=(double) stasis_width*PerceptibleReciprocal((double)
1537  stasis_height);
1538  if (geometry_ratio >= image_ratio)
1539  {
1540  *width=stasis_width;
1541  *height=CastDoubleToUnsigned((double) (PerceptibleReciprocal(
1542  geometry_ratio)*stasis_height*image_ratio)+0.5);
1543  }
1544  else
1545  {
1546  *width=CastDoubleToUnsigned(PerceptibleReciprocal(image_ratio)*
1547  stasis_width*geometry_ratio+0.5);
1548  *height=stasis_height;
1549  }
1550  stasis_width=(*width);
1551  stasis_height=(*height);
1552  }
1553  if (((flags & AspectValue) != 0) || ((*width == stasis_width) &&
1554  (*height == stasis_height)))
1555  {
1556  if ((flags & RhoValue) == 0)
1557  *width=stasis_width;
1558  if ((flags & SigmaValue) == 0)
1559  *height=stasis_height;
1560  }
1561  else
1562  {
1563  double
1564  scale_factor;
1565 
1566  /*
1567  Respect aspect ratio of the image.
1568  */
1569  if ((stasis_width == 0) || (stasis_height == 0))
1570  scale_factor=1.0;
1571  else
1572  if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1573  {
1574  scale_factor=(double) *width/(double) stasis_width;
1575  if ((flags & MinimumValue) == 0)
1576  {
1577  if (scale_factor > ((double) *height/(double) stasis_height))
1578  scale_factor=(double) *height/(double) stasis_height;
1579  }
1580  else
1581  if (scale_factor < ((double) *height/(double) stasis_height))
1582  scale_factor=(double) *height/(double) stasis_height;
1583  }
1584  else
1585  if ((flags & RhoValue) != 0)
1586  {
1587  scale_factor=(double) *width/(double) stasis_width;
1588  if (((flags & MinimumValue) != 0) &&
1589  (scale_factor < ((double) *width/(double) stasis_height)))
1590  scale_factor=(double) *width/(double) stasis_height;
1591  }
1592  else
1593  {
1594  scale_factor=(double) *height/(double) stasis_height;
1595  if (((flags & MinimumValue) != 0) &&
1596  (scale_factor < ((double) *height/(double) stasis_width)))
1597  scale_factor=(double) *height/(double) stasis_width;
1598  }
1599  *width=CastDoubleToUnsigned(MagickMax(floor(scale_factor*stasis_width+
1600  0.5),1.0));
1601  *height=CastDoubleToUnsigned(MagickMax(floor(scale_factor*stasis_height+
1602  0.5),1.0));
1603  }
1604  if ((flags & GreaterValue) != 0)
1605  {
1606  if (stasis_width < *width)
1607  *width=stasis_width;
1608  if (stasis_height < *height)
1609  *height=stasis_height;
1610  }
1611  if ((flags & LessValue) != 0)
1612  {
1613  if (stasis_width > *width)
1614  *width=stasis_width;
1615  if (stasis_height > *height)
1616  *height=stasis_height;
1617  }
1618  if ((flags & AreaValue) != 0)
1619  {
1620  double
1621  area,
1622  distance;
1623 
1624  PointInfo
1625  scale;
1626 
1627  /*
1628  Geometry is a maximum area in pixels.
1629  */
1630  (void) ParseGeometry(geometry,&geometry_info);
1631  area=geometry_info.rho+sqrt(MagickEpsilon);
1632  distance=sqrt((double) stasis_width*stasis_height);
1633  scale.x=(double) stasis_width*PerceptibleReciprocal(distance*
1634  PerceptibleReciprocal(sqrt(area)));
1635  scale.y=(double) stasis_height*PerceptibleReciprocal(distance*
1636  PerceptibleReciprocal(sqrt(area)));
1637  if ((scale.x < (double) *width) || (scale.y < (double) *height))
1638  {
1639  *width=CastDoubleToUnsigned(stasis_width*PerceptibleReciprocal(
1640  distance*PerceptibleReciprocal(sqrt(area)))+0.5);
1641  *height=CastDoubleToUnsigned(stasis_height*PerceptibleReciprocal(
1642  distance*PerceptibleReciprocal(sqrt(area)))+0.5);
1643  }
1644  }
1645  return(flags);
1646 }
1647 
1648 /*
1649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1650 % %
1651 % %
1652 % %
1653 % P a r s e P a g e G e o m e t r y %
1654 % %
1655 % %
1656 % %
1657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1658 %
1659 % ParsePageGeometry() returns a region as defined by the geometry string with
1660 % respect to the image page (canvas) dimensions.
1661 %
1662 % WARNING: Percentage dimensions remain relative to the actual image
1663 % dimensions, and not canvas dimensions.
1664 %
1665 % The format of the ParsePageGeometry method is:
1666 %
1667 % MagickStatusType ParsePageGeometry(const Image *image,
1668 % const char *geometry,RectangleInfo *region_info,
1669 % ExceptionInfo *exception)
1670 %
1671 % A description of each parameter follows:
1672 %
1673 % o geometry: The geometry string (e.g. "100x100+10+10").
1674 %
1675 % o region_info: the region as defined by the geometry string with
1676 % respect to the image and its gravity.
1677 %
1678 % o exception: return any errors or warnings in this structure.
1679 %
1680 */
1681 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1682  const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1683 {
1684  MagickStatusType
1685  flags;
1686 
1687  SetGeometry(image,region_info);
1688  if (image->page.width != 0)
1689  region_info->width=image->page.width;
1690  if (image->page.height != 0)
1691  region_info->height=image->page.height;
1692  flags=ParseAbsoluteGeometry(geometry,region_info);
1693  if (flags == NoValue)
1694  {
1695  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1696  "InvalidGeometry","`%s'",geometry);
1697  return(flags);
1698  }
1699  if ((flags & PercentValue) != 0)
1700  {
1701  region_info->width=image->columns;
1702  region_info->height=image->rows;
1703  }
1704  flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1705  &region_info->width,&region_info->height);
1706  if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1707  (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1708  {
1709  if ((flags & WidthValue) == 0)
1710  region_info->width=region_info->height;
1711  if ((flags & HeightValue) == 0)
1712  region_info->height=region_info->width;
1713  }
1714  return(flags);
1715 }
1716 
1717 /*
1718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719 % %
1720 % %
1721 % %
1722 % P a r s e R e g i o n G e o m e t r y %
1723 % %
1724 % %
1725 % %
1726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727 %
1728 % ParseRegionGeometry() returns a region as defined by the geometry string
1729 % with respect to the image dimensions and aspect ratio.
1730 %
1731 % This is basically a wrapper around ParseMetaGeometry. This is typically
1732 % used to parse a geometry string to work out the final integer dimensions
1733 % for image resizing.
1734 %
1735 % The format of the ParseRegionGeometry method is:
1736 %
1737 % MagickStatusType ParseRegionGeometry(const Image *image,
1738 % const char *geometry,RectangleInfo *region_info,
1739 % ExceptionInfo *exception)
1740 %
1741 % A description of each parameter follows:
1742 %
1743 % o geometry: The geometry string (e.g. "100x100+10+10").
1744 %
1745 % o region_info: the region as defined by the geometry string.
1746 %
1747 % o exception: return any errors or warnings in this structure.
1748 %
1749 */
1750 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1751  const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1752 {
1753  MagickStatusType
1754  flags;
1755 
1756  SetGeometry(image,region_info);
1757  flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1758  &region_info->width,&region_info->height);
1759  if (flags == NoValue)
1760  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1761  "InvalidGeometry","`%s'",geometry);
1762  return(flags);
1763 }
1764 
1765 /*
1766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1767 % %
1768 % %
1769 % %
1770 % S e t G e o m e t r y %
1771 % %
1772 % %
1773 % %
1774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775 %
1776 % SetGeometry() sets the geometry to its default values.
1777 %
1778 % The format of the SetGeometry method is:
1779 %
1780 % SetGeometry(const Image *image,RectangleInfo *geometry)
1781 %
1782 % A description of each parameter follows:
1783 %
1784 % o image: the image.
1785 %
1786 % o geometry: the geometry.
1787 %
1788 */
1789 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1790 {
1791  assert(image != (Image *) NULL);
1792  assert(image->signature == MagickCoreSignature);
1793  if (IsEventLogging() != MagickFalse)
1794  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1795  assert(geometry != (RectangleInfo *) NULL);
1796  (void) memset(geometry,0,sizeof(*geometry));
1797  geometry->width=image->columns;
1798  geometry->height=image->rows;
1799 }
1800 
1801 /*
1802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1803 % %
1804 % %
1805 % %
1806 % S e t G e o m e t r y I n f o %
1807 % %
1808 % %
1809 % %
1810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1811 %
1812 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1813 %
1814 % The format of the SetGeometryInfo method is:
1815 %
1816 % SetGeometryInfo(GeometryInfo *geometry_info)
1817 %
1818 % A description of each parameter follows:
1819 %
1820 % o geometry_info: the geometry info structure.
1821 %
1822 */
1823 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1824 {
1825  assert(geometry_info != (GeometryInfo *) NULL);
1826  if (IsEventLogging() != MagickFalse)
1827  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1828  (void) memset(geometry_info,0,sizeof(*geometry_info));
1829 }
_AffineMatrix
Definition: geometry.h:94
_RectangleInfo
Definition: geometry.h:129
_GeometryInfo
Definition: geometry.h:105
_PageInfo
Definition: geometry.c:61
_Image
Definition: image.h:131
_ExceptionInfo
Definition: exception.h:101
_PointInfo
Definition: geometry.h:122