2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Geometry Methods %
20 % Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % https://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/constitute.h"
44 #include "MagickCore/draw.h"
45 #include "MagickCore/exception.h"
46 #include "MagickCore/exception-private.h"
47 #include "MagickCore/geometry.h"
48 #include "MagickCore/image-private.h"
49 #include "MagickCore/memory_.h"
50 #include "MagickCore/string_.h"
51 #include "MagickCore/string-private.h"
52 #include "MagickCore/token.h"
55 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59 % G e t G e o m e t r y %
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 % GetGeometry() parses a geometry specification and returns the width,
66 % height, x, and y values. It also returns flags that indicates which
67 % of the four values (width, height, x, y) were located in the string, and
68 % whether the x or y values are negative. In addition, there are flags to
69 % report any meta characters (%, !, <, or >).
71 % The value must form a proper geometry style specification of WxH+X+Y
72 % of integers only, and values can not be separated by comma, colon, or
73 % slash charcaters. See ParseGeometry() below.
75 % Offsets may be prefixed by multiple signs to make offset string
76 % substitutions easier to handle from shell scripts.
77 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
78 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
81 % The format of the GetGeometry method is:
83 % MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
84 % size_t *width,size_t *height)
86 % A description of each parameter follows:
88 % o geometry: The geometry.
90 % o x,y: The x and y offset as determined by the geometry specification.
92 % o width,height: The width and height as determined by the geometry
96 MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
97 ssize_t *y,size_t *width,size_t *height)
101 pedantic_geometry[MagickPathExtent],
114 Remove whitespace and meta characters from geometry specification.
117 if ((geometry == (char *) NULL) || (*geometry == '\0'))
119 if (strlen(geometry) >= (MagickPathExtent-1))
121 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
122 for (p=pedantic_geometry; *p != '\0'; )
124 if (isspace((int) ((unsigned char) *p)) != 0)
126 (void) CopyMagickString(p,p+1,MagickPathExtent);
135 (void) CopyMagickString(p,p+1,MagickPathExtent);
141 (void) CopyMagickString(p,p+1,MagickPathExtent);
147 (void) CopyMagickString(p,p+1,MagickPathExtent);
153 (void) CopyMagickString(p,p+1,MagickPathExtent);
159 (void) CopyMagickString(p,p+1,MagickPathExtent);
165 (void) CopyMagickString(p,p+1,MagickPathExtent);
171 (void) CopyMagickString(p,p+1,MagickPathExtent);
177 flags|=SeparatorValue;
207 Parse width, height, x, and y.
213 value=StringToDouble(p,&q);
215 if (LocaleNCompare(p,"0x",2) == 0)
216 value=(double) strtol(p,&q,10);
217 if ((*p != '+') && (*p != '-'))
219 c=(int) ((unsigned char) *q);
220 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
226 if (width != (size_t *) NULL)
228 if (LocaleNCompare(p,"0x",2) == 0)
229 *width=(size_t) strtol(p,&p,10);
231 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
237 if ((*p != '+') && (*p != '-'))
239 c=(int) ((unsigned char) *p);
240 if ((c == 215) || (*p == 'x') || (*p == 'X'))
243 if ((*p != '+') && (*p != '-'))
249 if (height != (size_t *) NULL)
250 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
256 if ((*p == '+') || (*p == '-'))
261 while ((*p == '+') || (*p == '-'))
264 flags^=XNegative; /* negate sign */
268 if (x != (ssize_t *) NULL)
269 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
273 if (((flags & XNegative) != 0) && (x != (ssize_t *) NULL))
277 if ((*p == '+') || (*p == '-'))
282 while ((*p == '+') || (*p == '-'))
285 flags^=YNegative; /* negate sign */
289 if (y != (ssize_t *) NULL)
290 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
294 if (((flags & YNegative) != 0) && (y != (ssize_t *) NULL))
298 if ((flags & PercentValue) != 0)
300 if (((flags & SeparatorValue) == 0) && ((flags & HeightValue) == 0))
302 if ((height != (size_t *) NULL) && (width != (size_t *) NULL))
306 if (((flags & SeparatorValue) != 0) && ((flags & WidthValue) == 0) &&
307 (height != (size_t *) NULL) && (width != (size_t *) NULL))
311 /* Debugging Geometry */
312 (void) fprintf(stderr,"GetGeometry...\n");
313 (void) fprintf(stderr,"Input: %s\n",geometry);
314 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
315 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
316 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
317 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
318 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
319 *height,(long) *x,(long) *y);
325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329 % G e t P a g e G e o m e t r y %
333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 % GetPageGeometry() replaces any page mneumonic with the equivalent size in
338 % The format of the GetPageGeometry method is:
340 % char *GetPageGeometry(const char *page_geometry)
342 % A description of each parameter follows.
344 % o page_geometry: Specifies a pointer to an array of characters. The
345 % string is either a Postscript page name (e.g. A4) or a postscript page
346 % geometry (e.g. 612x792+36+36).
349 MagickExport char *GetPageGeometry(const char *page_geometry)
351 #define MagickPageSize(name,geometry) { (name), sizeof(name)-1, (geometry) }
353 typedef struct _PageInfo
365 static const PageInfo
368 MagickPageSize("4x6", "288x432"),
369 MagickPageSize("5x7", "360x504"),
370 MagickPageSize("7x9", "504x648"),
371 MagickPageSize("8x10", "576x720"),
372 MagickPageSize("9x11", "648x792"),
373 MagickPageSize("9x12", "648x864"),
374 MagickPageSize("10x13", "720x936"),
375 MagickPageSize("10x14", "720x1008"),
376 MagickPageSize("11x17", "792x1224"),
377 MagickPageSize("a0", "2384x3370"),
378 MagickPageSize("a1", "1684x2384"),
379 MagickPageSize("a10", "73x105"),
380 MagickPageSize("a2", "1191x1684"),
381 MagickPageSize("a3", "842x1191"),
382 MagickPageSize("a4", "595x842"),
383 MagickPageSize("a4small", "595x842"),
384 MagickPageSize("a5", "420x595"),
385 MagickPageSize("a6", "297x420"),
386 MagickPageSize("a7", "210x297"),
387 MagickPageSize("a8", "148x210"),
388 MagickPageSize("a9", "105x148"),
389 MagickPageSize("archa", "648x864"),
390 MagickPageSize("archb", "864x1296"),
391 MagickPageSize("archC", "1296x1728"),
392 MagickPageSize("archd", "1728x2592"),
393 MagickPageSize("arche", "2592x3456"),
394 MagickPageSize("b0", "2920x4127"),
395 MagickPageSize("b1", "2064x2920"),
396 MagickPageSize("b10", "91x127"),
397 MagickPageSize("b2", "1460x2064"),
398 MagickPageSize("b3", "1032x1460"),
399 MagickPageSize("b4", "729x1032"),
400 MagickPageSize("b5", "516x729"),
401 MagickPageSize("b6", "363x516"),
402 MagickPageSize("b7", "258x363"),
403 MagickPageSize("b8", "181x258"),
404 MagickPageSize("b9", "127x181"),
405 MagickPageSize("c0", "2599x3676"),
406 MagickPageSize("c1", "1837x2599"),
407 MagickPageSize("c2", "1298x1837"),
408 MagickPageSize("c3", "918x1296"),
409 MagickPageSize("c4", "649x918"),
410 MagickPageSize("c5", "459x649"),
411 MagickPageSize("c6", "323x459"),
412 MagickPageSize("c7", "230x323"),
413 MagickPageSize("executive", "540x720"),
414 MagickPageSize("flsa", "612x936"),
415 MagickPageSize("flse", "612x936"),
416 MagickPageSize("folio", "612x936"),
417 MagickPageSize("halfletter", "396x612"),
418 MagickPageSize("isob0", "2835x4008"),
419 MagickPageSize("isob1", "2004x2835"),
420 MagickPageSize("isob10", "88x125"),
421 MagickPageSize("isob2", "1417x2004"),
422 MagickPageSize("isob3", "1001x1417"),
423 MagickPageSize("isob4", "709x1001"),
424 MagickPageSize("isob5", "499x709"),
425 MagickPageSize("isob6", "354x499"),
426 MagickPageSize("isob7", "249x354"),
427 MagickPageSize("isob8", "176x249"),
428 MagickPageSize("isob9", "125x176"),
429 MagickPageSize("jisb0", "1030x1456"),
430 MagickPageSize("jisb1", "728x1030"),
431 MagickPageSize("jisb2", "515x728"),
432 MagickPageSize("jisb3", "364x515"),
433 MagickPageSize("jisb4", "257x364"),
434 MagickPageSize("jisb5", "182x257"),
435 MagickPageSize("jisb6", "128x182"),
436 MagickPageSize("ledger", "1224x792"),
437 MagickPageSize("legal", "612x1008"),
438 MagickPageSize("letter", "612x792"),
439 MagickPageSize("lettersmall", "612x792"),
440 MagickPageSize("quarto", "610x780"),
441 MagickPageSize("statement", "396x612"),
442 MagickPageSize("tabloid", "792x1224")
451 assert(page_geometry != (char *) NULL);
452 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
453 CopyMagickString(page,page_geometry,MaxTextExtent);
454 for (i=0; i < (ssize_t) (sizeof(PageSizes)/sizeof(PageSizes[0])); i++)
459 status=LocaleNCompare(PageSizes[i].name,page_geometry,PageSizes[i].extent);
469 Replace mneumonic with the equivalent size in dots-per-inch.
471 (void) FormatLocaleString(page,MaxTextExtent,"%s%.80s",
472 PageSizes[i].geometry,page_geometry+PageSizes[i].extent);
473 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
475 if ((flags & GreaterValue) == 0)
476 (void) ConcatenateMagickString(page,">",MaxTextExtent);
480 return(AcquireString(page));
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488 % G r a v i t y A d j u s t G e o m e t r y %
492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494 % GravityAdjustGeometry() adjusts the offset of a region with regard to the
495 % given: width, height and gravity; against which it is positioned.
497 % The region should also have an appropriate width and height to correctly
498 % set the right offset of the top left corner of the region.
500 % The format of the GravityAdjustGeometry method is:
502 % void GravityAdjustGeometry(const size_t width, const size_t height,
503 % const GravityType gravity,RectangleInfo *region);
505 % A description of each parameter follows:
507 % o width, height: the larger area the region is relative to
509 % o gravity: the edge/corner the current offset is relative to
511 % o region: The region requiring a offset adjustment relative to gravity
514 MagickExport void GravityAdjustGeometry(const size_t width,
515 const size_t height,const GravityType gravity,RectangleInfo *region)
517 if (region->height == 0)
518 region->height=height;
519 if (region->width == 0)
523 case NorthEastGravity:
525 case SouthEastGravity:
527 region->x=(ssize_t) (width-region->width-region->x);
534 region->x+=(ssize_t) (width/2-region->width/2);
538 case NorthWestGravity:
540 case SouthWestGravity:
546 case SouthWestGravity:
548 case SouthEastGravity:
550 region->y=(ssize_t) (height-region->height-region->y);
557 region->y+=(ssize_t) (height/2-region->height/2);
561 case NorthWestGravity:
563 case NorthEastGravity:
571 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575 + I s G e o m e t r y %
579 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
581 % IsGeometry() returns MagickTrue if the geometry specification is valid.
582 % Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
584 % The format of the IsGeometry method is:
586 % MagickBooleanType IsGeometry(const char *geometry)
588 % A description of each parameter follows:
590 % o geometry: This string is the geometry specification.
593 MagickExport MagickBooleanType IsGeometry(const char *geometry)
601 if (geometry == (const char *) NULL)
603 flags=ParseGeometry(geometry,&geometry_info);
604 return(flags != NoValue ? MagickTrue : MagickFalse);
608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
612 + I s S c e n e G e o m e t r y %
616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618 % IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
619 % specification (e.g. [1], [1-9], [1,7,4]).
621 % The format of the IsSceneGeometry method is:
623 % MagickBooleanType IsSceneGeometry(const char *geometry,
624 % const MagickBooleanType pedantic)
626 % A description of each parameter follows:
628 % o geometry: This string is the geometry specification.
630 % o pedantic: A value other than 0 invokes a more restrictive set of
631 % conditions for a valid specification (e.g. [1], [1-4], [4-1]).
634 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
635 const MagickBooleanType pedantic)
643 if (geometry == (const char *) NULL)
646 value=StringToDouble(geometry,&p);
650 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
652 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662 % P a r s e A b s o l u t e G e o m e t r y %
666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668 % ParseAbsoluteGeometry() returns a region as defined by the geometry string,
669 % without any modification by percentages or gravity.
671 % It currently just a wrapper around GetGeometry(), but may be expanded in
672 % the future to handle other positioning information.
674 % The format of the ParseAbsoluteGeometry method is:
676 % MagickStatusType ParseAbsoluteGeometry(const char *geometry,
677 % RectangleInfo *region_info)
679 % A description of each parameter follows:
681 % o geometry: The geometry string (e.g. "100x100+10+10").
683 % o region_info: the region as defined by the geometry string.
686 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
687 RectangleInfo *region_info)
692 flags=GetGeometry(geometry,®ion_info->x,®ion_info->y,
693 ®ion_info->width,®ion_info->height);
698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
702 % P a r s e A f f i n e G e o m e t r y %
706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
708 % ParseAffineGeometry() returns an affine matrix as defined by a string of 4
709 % to 6 comma/space separated floating point values.
711 % The affine matrix determinant is checked for validity of the values.
713 % The format of the ParseAffineGeometry method is:
715 % MagickStatusType ParseAffineGeometry(const char *geometry,
716 % AffineMatrix *affine_matrix,ExceptionInfo *exception)
718 % A description of each parameter follows:
720 % o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
722 % o affine_matrix: the affine matrix as defined by the geometry string.
724 % o exception: return any errors or warnings in this structure.
727 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
728 AffineMatrix *affine_matrix,ExceptionInfo *exception)
731 token[MagickPathExtent];
745 GetAffineMatrix(affine_matrix);
748 for (i=0; (*p != '\0') && (i < 6); i++)
750 GetNextToken(p,&p,MagickPathExtent,token);
752 GetNextToken(p,&p,MagickPathExtent,token);
757 affine_matrix->sx=StringToDouble(token,(char **) NULL);
762 affine_matrix->rx=StringToDouble(token,(char **) NULL);
767 affine_matrix->ry=StringToDouble(token,(char **) NULL);
772 affine_matrix->sy=StringToDouble(token,(char **) NULL);
777 affine_matrix->tx=StringToDouble(token,(char **) NULL);
783 affine_matrix->ty=StringToDouble(token,(char **) NULL);
789 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
791 if (fabs(determinant) < MagickEpsilon)
792 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
793 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802 % P a r s e G e o m e t r y %
806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 % ParseGeometry() parses a geometry specification and returns the sigma,
809 % rho, xi, and psi values. It also returns flags that indicates which
810 % of the four values (sigma, rho, xi, psi) were located in the string, and
811 % whether the xi or pi values are negative.
813 % In addition, it reports if there are any of meta characters (%, !, <, >, @,
814 % and ^) flags present. It does not report the location of the percentage
815 % relative to the values.
817 % Values may also be separated by commas, colons, or slashes, and offsets.
818 % Offsets may be prefixed by multiple signs to make offset string
819 % substitutions easier to handle from shell scripts.
820 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
821 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
824 % The format of the ParseGeometry method is:
826 % MagickStatusType ParseGeometry(const char *geometry,
827 % GeometryInfo *geometry_info)
829 % A description of each parameter follows:
831 % o geometry: The geometry string (e.g. "100x100+10+10").
833 % o geometry_info: returns the parsed width/height/x/y in this structure.
836 MagickExport MagickStatusType ParseGeometry(const char *geometry,
837 GeometryInfo *geometry_info)
841 pedantic_geometry[MagickPathExtent],
857 Remove whitespaces meta characters from geometry specification.
859 assert(geometry_info != (GeometryInfo *) NULL);
861 if ((geometry == (char *) NULL) || (*geometry == '\0'))
863 if (strlen(geometry) >= (MagickPathExtent-1))
865 c=sscanf(geometry,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",&coordinate.rho,
866 &coordinate.sigma,&coordinate.xi,&coordinate.psi);
870 Special case: coordinate (e.g. 0,0 255,255).
872 geometry_info->rho=coordinate.rho;
873 geometry_info->sigma=coordinate.sigma;
874 geometry_info->xi=coordinate.xi;
875 geometry_info->psi=coordinate.psi;
876 flags|=RhoValue | SigmaValue | XiValue | PsiValue;
879 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
880 for (p=pedantic_geometry; *p != '\0'; )
882 c=(int) ((unsigned char) *p);
885 (void) CopyMagickString(p,p+1,MagickPathExtent);
893 (void) CopyMagickString(p,p+1,MagickPathExtent);
899 (void) CopyMagickString(p,p+1,MagickPathExtent);
905 (void) CopyMagickString(p,p+1,MagickPathExtent);
911 (void) CopyMagickString(p,p+1,MagickPathExtent);
917 (void) CopyMagickString(p,p+1,MagickPathExtent);
923 (void) CopyMagickString(p,p+1,MagickPathExtent);
929 (void) CopyMagickString(p,p+1,MagickPathExtent);
935 flags|=SeparatorValue;
972 Parse rho, sigma, xi, psi, and optionally chi.
978 value=StringToDouble(p,&q);
979 if (LocaleNCompare(p,"0x",2) == 0)
980 (void) strtol(p,&q,10);
981 c=(int) ((unsigned char) *q);
982 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
983 (*q == '/') || (*q == ':') || (*q =='\0'))
989 if (LocaleNCompare(p,"0x",2) == 0)
990 value=(double) strtol(p,&p,10);
992 value=StringToDouble(p,&p);
996 geometry_info->rho=value;
1000 c=(int) ((unsigned char) *p);
1001 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
1008 while (isspace((int) ((unsigned char) *p)) != 0)
1010 c=(int) ((unsigned char) *q);
1011 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
1015 value=StringToDouble(p,&p);
1019 geometry_info->sigma=value;
1023 while (isspace((int) ((unsigned char) *p)) != 0)
1025 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1030 if ((*p == ',') || (*p == '/') || (*p == ':') )
1032 while ((*p == '+') || (*p == '-'))
1035 flags^=XiNegative; /* negate sign */
1039 value=StringToDouble(p,&p);
1043 if ((flags & XiNegative) != 0)
1045 geometry_info->xi=value;
1047 while (isspace((int) ((unsigned char) *p)) != 0)
1049 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1055 if ((*p == ',') || (*p == '/') || (*p == ':'))
1057 while ((*p == '+') || (*p == '-'))
1060 flags^=PsiNegative; /* negate sign */
1064 value=StringToDouble(p,&p);
1068 if ((flags & PsiNegative) != 0)
1070 geometry_info->psi=value;
1073 while (isspace((int) ((unsigned char) *p)) != 0)
1075 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1081 if ((*p == ',') || (*p == '/') || (*p == ':'))
1083 while ((*p == '+') || (*p == '-'))
1086 flags^=ChiNegative; /* negate sign */
1090 value=StringToDouble(p,&p);
1094 if ((flags & ChiNegative) != 0)
1096 geometry_info->chi=value;
1100 if (strchr(pedantic_geometry,':') != (char *) NULL)
1103 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1105 geometry_info->rho/=geometry_info->sigma;
1106 geometry_info->sigma=1.0;
1107 if (geometry_info->xi == 0.0)
1108 geometry_info->sigma=2.0;
1110 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1111 ((flags & PsiValue) == 0))
1114 Support negative height values (e.g. 30x-20).
1116 geometry_info->sigma=geometry_info->xi;
1117 geometry_info->xi=0.0;
1121 if ((flags & PercentValue) != 0)
1123 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1124 geometry_info->sigma=geometry_info->rho;
1125 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1126 geometry_info->rho=geometry_info->sigma;
1129 /* Debugging Geometry */
1130 (void) fprintf(stderr,"ParseGeometry...\n");
1131 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1132 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1133 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1134 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1135 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1136 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1137 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1138 geometry_info->chi);
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1148 % P a r s e G r a v i t y G e o m e t r y %
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1154 % ParseGravityGeometry() returns a region as defined by the geometry string
1155 % with respect to the given image page (canvas) dimensions and the images
1158 % This is typically used for specifing a area within a given image for
1159 % cropping images to a smaller size, chopping out rows and or columns, or
1160 % resizing and positioning overlay images.
1162 % Percentages are relative to image size and not page size, and are set to
1163 % nearest integer (pixel) size.
1165 % The format of the ParseGravityGeometry method is:
1167 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1168 % RectangeInfo *region_info,ExceptionInfo *exception)
1170 % A description of each parameter follows:
1172 % o geometry: The geometry string (e.g. "100x100+10+10").
1174 % o region_info: the region as defined by the geometry string with respect
1175 % to the image dimensions and its gravity.
1177 % o exception: return any errors or warnings in this structure.
1180 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1181 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1190 SetGeometry(image,region_info);
1191 if (image->page.width != 0)
1192 region_info->width=image->page.width;
1193 if (image->page.height != 0)
1194 region_info->height=image->page.height;
1195 flags=ParseAbsoluteGeometry(geometry,region_info);
1196 if (flags == NoValue)
1198 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1199 "InvalidGeometry","`%s'",geometry);
1202 if ((flags & PercentValue) != 0)
1214 Geometry is a percentage of the image size, not canvas size
1216 if (image->gravity != UndefinedGravity)
1217 flags|=XValue | YValue;
1218 status=ParseGeometry(geometry,&geometry_info);
1219 scale.x=geometry_info.rho;
1220 if ((status & RhoValue) == 0)
1222 scale.y=geometry_info.sigma;
1223 if ((status & SigmaValue) == 0)
1225 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1226 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
1229 Adjust offset according to gravity setting.
1231 width=region_info->width;
1232 height=region_info->height;
1234 region_info->width=image->page.width | image->columns;
1236 region_info->height=image->page.height | image->rows;
1237 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1238 region_info->width=width;
1239 region_info->height=height;
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 + P a r s e M e t a G e o m e t r y %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1255 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1256 % and ^ in relation to image resizing.
1258 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1259 % much as possible, while generating a integer (pixel) size, and fitting the
1260 % image within the specified geometry width and height.
1262 % Flags are interpreted...
1263 % % geometry size is given percentage of original width and height given
1264 % ! do not try to preserve aspect ratio
1265 % < only enlarge images smaller that geometry
1266 % > only shrink images larger than geometry
1267 % @ Fit image to contain at most this many pixels
1268 % ^ Contain the given geometry given, (minimal dimensions given)
1270 % The format of the ParseMetaGeometry method is:
1272 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1273 % ssize_t *y, size_t *width,size_t *height)
1275 % A description of each parameter follows:
1277 % o geometry: The geometry string (e.g. "100x100+10+10").
1279 % o x,y: The x and y offset, set according to the geometry specification.
1281 % o width,height: The width and height of original image, modified by
1282 % the given geometry specification.
1286 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1287 ssize_t *y,size_t *width,size_t *height)
1300 Ensure the image geometry is valid.
1302 assert(x != (ssize_t *) NULL);
1303 assert(y != (ssize_t *) NULL);
1304 assert(width != (size_t *) NULL);
1305 assert(height != (size_t *) NULL);
1306 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1310 Parse geometry using GetGeometry.
1312 SetGeometryInfo(&geometry_info);
1313 former_width=(*width);
1314 former_height=(*height);
1315 flags=GetGeometry(geometry,x,y,width,height);
1316 if ((flags & PercentValue) != 0)
1325 Geometry is a percentage of the image size.
1327 percent_flags=ParseGeometry(geometry,&geometry_info);
1328 scale.x=geometry_info.rho;
1329 if ((percent_flags & RhoValue) == 0)
1331 scale.y=geometry_info.sigma;
1332 if ((percent_flags & SigmaValue) == 0)
1334 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
1335 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
1336 former_width=(*width);
1337 former_height=(*height);
1339 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1340 (*height == former_height)))
1342 if ((flags & RhoValue) == 0)
1343 *width=former_width;
1344 if ((flags & SigmaValue) == 0)
1345 *height=former_height;
1353 Respect aspect ratio of the image.
1355 if ((former_width == 0) || (former_height == 0))
1358 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1360 scale_factor=(double) *width/(double) former_width;
1361 if ((flags & MinimumValue) == 0)
1363 if (scale_factor > ((double) *height/(double) former_height))
1364 scale_factor=(double) *height/(double) former_height;
1367 if (scale_factor < ((double) *height/(double) former_height))
1368 scale_factor=(double) *height/(double) former_height;
1371 if ((flags & RhoValue) != 0)
1373 scale_factor=(double) *width/(double) former_width;
1374 if (((flags & MinimumValue) != 0) &&
1375 (scale_factor < ((double) *width/(double) former_height)))
1376 scale_factor=(double) *width/(double) former_height;
1380 scale_factor=(double) *height/(double) former_height;
1381 if (((flags & MinimumValue) != 0) &&
1382 (scale_factor < ((double) *height/(double) former_width)))
1383 scale_factor=(double) *height/(double) former_width;
1385 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1386 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
1388 if ((flags & GreaterValue) != 0)
1390 if (former_width < *width)
1391 *width=former_width;
1392 if (former_height < *height)
1393 *height=former_height;
1395 if ((flags & LessValue) != 0)
1397 if (former_width > *width)
1398 *width=former_width;
1399 if (former_height > *height)
1400 *height=former_height;
1402 if ((flags & AreaValue) != 0)
1412 Geometry is a maximum area in pixels.
1414 (void) ParseGeometry(geometry,&geometry_info);
1415 area=geometry_info.rho+sqrt(MagickEpsilon);
1416 distance=sqrt((double) former_width*former_height);
1417 scale.x=(double) former_width/(distance/sqrt(area));
1418 scale.y=(double) former_height/(distance/sqrt(area));
1419 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1421 *width=(unsigned long) (former_width/(distance/sqrt(area)));
1422 *height=(unsigned long) (former_height/(distance/sqrt(area)));
1424 former_width=(*width);
1425 former_height=(*height);
1431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435 % P a r s e P a g e G e o m e t r y %
1439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441 % ParsePageGeometry() returns a region as defined by the geometry string with
1442 % respect to the image page (canvas) dimensions.
1444 % WARNING: Percentage dimensions remain relative to the actual image
1445 % dimensions, and not canvas dimensions.
1447 % The format of the ParsePageGeometry method is:
1449 % MagickStatusType ParsePageGeometry(const Image *image,
1450 % const char *geometry,RectangeInfo *region_info,
1451 % ExceptionInfo *exception)
1453 % A description of each parameter follows:
1455 % o geometry: The geometry string (e.g. "100x100+10+10").
1457 % o region_info: the region as defined by the geometry string with
1458 % respect to the image and its gravity.
1460 % o exception: return any errors or warnings in this structure.
1463 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1464 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1469 SetGeometry(image,region_info);
1470 if (image->page.width != 0)
1471 region_info->width=image->page.width;
1472 if (image->page.height != 0)
1473 region_info->height=image->page.height;
1474 flags=ParseAbsoluteGeometry(geometry,region_info);
1475 if (flags == NoValue)
1477 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1478 "InvalidGeometry","`%s'",geometry);
1481 if ((flags & PercentValue) != 0)
1483 region_info->width=image->columns;
1484 region_info->height=image->rows;
1486 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1487 ®ion_info->width,®ion_info->height);
1488 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1489 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1491 if ((flags & WidthValue) == 0)
1492 region_info->width=region_info->height;
1493 if ((flags & HeightValue) == 0)
1494 region_info->height=region_info->width;
1500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1504 % P a r s e R e g i o n G e o m e t r y %
1508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1510 % ParseRegionGeometry() returns a region as defined by the geometry string
1511 % with respect to the image dimensions and aspect ratio.
1513 % This is basically a wrapper around ParseMetaGeometry. This is typically
1514 % used to parse a geometry string to work out the final integer dimensions
1515 % for image resizing.
1517 % The format of the ParseRegionGeometry method is:
1519 % MagickStatusType ParseRegionGeometry(const Image *image,
1520 % const char *geometry,RectangeInfo *region_info,
1521 % ExceptionInfo *exception)
1523 % A description of each parameter follows:
1525 % o geometry: The geometry string (e.g. "100x100+10+10").
1527 % o region_info: the region as defined by the geometry string.
1529 % o exception: return any errors or warnings in this structure.
1532 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1533 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1538 SetGeometry(image,region_info);
1539 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1540 ®ion_info->width,®ion_info->height);
1541 if (flags == NoValue)
1542 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1543 "InvalidGeometry","`%s'",geometry);
1548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1552 % S e t G e o m e t r y %
1556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1558 % SetGeometry() sets the geometry to its default values.
1560 % The format of the SetGeometry method is:
1562 % SetGeometry(const Image *image,RectangleInfo *geometry)
1564 % A description of each parameter follows:
1566 % o image: the image.
1568 % o geometry: the geometry.
1571 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1573 assert(image != (Image *) NULL);
1574 assert(image->signature == MagickCoreSignature);
1575 if (image->debug != MagickFalse)
1576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1577 assert(geometry != (RectangleInfo *) NULL);
1578 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1579 geometry->width=image->columns;
1580 geometry->height=image->rows;
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1588 % S e t G e o m e t r y I n f o %
1592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1596 % The format of the SetGeometryInfo method is:
1598 % SetGeometryInfo(GeometryInfo *geometry_info)
1600 % A description of each parameter follows:
1602 % o geometry_info: the geometry info structure.
1605 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1607 assert(geometry_info != (GeometryInfo *) NULL);
1608 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1609 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));