blob: baeb0b04f742b042459f05d9ef441214982a91f1 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% January 2003 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% http://www.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*/
cristy4c08aed2011-07-01 19:47:50 +000042#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/memory_.h"
49#include "MagickCore/string_.h"
50#include "MagickCore/string-private.h"
51#include "MagickCore/token.h"
cristy3ed852e2009-09-05 21:47:34 +000052
53/*
54%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55% %
56% %
57% %
58% G e t G e o m e t r y %
59% %
60% %
61% %
62%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63%
64% GetGeometry() parses a geometry specification and returns the width,
65% height, x, and y values. It also returns flags that indicates which
66% of the four values (width, height, x, y) were located in the string, and
67% whether the x or y values are negative. In addition, there are flags to
68% report any meta characters (%, !, <, or >).
69%
anthony6634c532012-06-15 06:56:39 +000070% The value must form a proper geometry style specification of WxH+X+Y
71% of integers only, and values can not be separated by comma, colon, or
72% slash charcaters. See ParseGeometry() below.
73%
74% Offsets may be prefixed by multiple signs to make offset string
75% substitutions easier to handle from shell scripts.
76% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
77% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
78% offsets.
79%
cristy3ed852e2009-09-05 21:47:34 +000080% The format of the GetGeometry method is:
81%
cristybb503372010-05-27 20:51:26 +000082% MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
83% size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000084%
85% A description of each parameter follows:
86%
87% o geometry: The geometry.
88%
89% o x,y: The x and y offset as determined by the geometry specification.
90%
91% o width,height: The width and height as determined by the geometry
92% specification.
93%
94*/
cristy272f23a2011-03-30 00:37:11 +000095MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
96 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000097{
98 char
99 *p,
100 pedantic_geometry[MaxTextExtent],
101 *q;
102
cristy00976d82011-02-20 20:31:28 +0000103 double
104 value;
105
cristyf30f47d2011-09-06 14:40:59 +0000106 int
107 c;
108
cristy3ed852e2009-09-05 21:47:34 +0000109 MagickStatusType
110 flags;
111
112 /*
113 Remove whitespace and meta characters from geometry specification.
114 */
115 flags=NoValue;
116 if ((geometry == (char *) NULL) || (*geometry == '\0'))
117 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000118 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000119 return(flags);
120 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
121 for (p=pedantic_geometry; *p != '\0'; )
122 {
123 if (isspace((int) ((unsigned char) *p)) != 0)
124 {
125 (void) CopyMagickString(p,p+1,MaxTextExtent);
126 continue;
127 }
anthonyc330e152012-04-19 14:40:36 +0000128 c=(int)*p;
cristy35f75ac2011-09-06 20:33:35 +0000129 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000130 {
131 case '%':
132 {
133 flags|=PercentValue;
134 (void) CopyMagickString(p,p+1,MaxTextExtent);
135 break;
136 }
137 case '!':
138 {
139 flags|=AspectValue;
140 (void) CopyMagickString(p,p+1,MaxTextExtent);
141 break;
142 }
143 case '<':
144 {
145 flags|=LessValue;
146 (void) CopyMagickString(p,p+1,MaxTextExtent);
147 break;
148 }
149 case '>':
150 {
151 flags|=GreaterValue;
152 (void) CopyMagickString(p,p+1,MaxTextExtent);
153 break;
154 }
155 case '^':
156 {
157 flags|=MinimumValue;
158 (void) CopyMagickString(p,p+1,MaxTextExtent);
159 break;
160 }
161 case '@':
162 {
163 flags|=AreaValue;
164 (void) CopyMagickString(p,p+1,MaxTextExtent);
165 break;
166 }
167 case '(':
168 case ')':
169 {
170 (void) CopyMagickString(p,p+1,MaxTextExtent);
171 break;
172 }
cristy1ad6c872012-08-02 11:54:05 +0000173 case 'x':
174 case 'X':
175 {
176 flags|=SeparatorValue;
177 p++;
178 break;
179 }
cristy3ed852e2009-09-05 21:47:34 +0000180 case '-':
181 case '.':
182 case ',':
183 case '+':
184 case '0':
185 case '1':
186 case '2':
187 case '3':
188 case '4':
189 case '5':
190 case '6':
191 case '7':
192 case '8':
193 case '9':
cristy35f75ac2011-09-06 20:33:35 +0000194 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000195 {
196 p++;
197 break;
198 }
199 default:
200 return(flags);
201 }
202 }
203 /*
204 Parse width, height, x, and y.
205 */
206 p=pedantic_geometry;
207 if (*p == '\0')
208 return(flags);
209 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000210 value=StringToDouble(p,&q);
cristy00976d82011-02-20 20:31:28 +0000211 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000212 if (LocaleNCompare(p,"0x",2) == 0)
cristyf53f26f2011-05-16 00:56:05 +0000213 value=(double) strtol(p,&q,10);
cristy57936e02012-07-03 00:33:28 +0000214 if ((*p != '+') && (*p != '-'))
cristy3ed852e2009-09-05 21:47:34 +0000215 {
cristy57936e02012-07-03 00:33:28 +0000216 c=(int) ((unsigned char) *q);
217 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
cristy3ed852e2009-09-05 21:47:34 +0000218 {
219 /*
cristy57936e02012-07-03 00:33:28 +0000220 Parse width.
cristy3ed852e2009-09-05 21:47:34 +0000221 */
222 q=p;
cristy57936e02012-07-03 00:33:28 +0000223 if (LocaleNCompare(p,"0x",2) == 0)
224 *width=(size_t) strtol(p,&p,10);
225 else
226 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000227 if (p != q)
cristy57936e02012-07-03 00:33:28 +0000228 flags|=WidthValue;
229 }
230 }
231 if ((*p != '+') && (*p != '-'))
232 {
233 c=(int) ((unsigned char) *p);
234 if ((c == 215) || (*p == 'x') || (*p == 'X'))
235 {
236 p++;
237 if ((*p != '+') && (*p != '-'))
238 {
239 /*
240 Parse height.
241 */
242 q=p;
243 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
244 if (p != q)
245 flags|=HeightValue;
246 }
cristy3ed852e2009-09-05 21:47:34 +0000247 }
248 }
249 if ((*p == '+') || (*p == '-'))
250 {
251 /*
252 Parse x value.
253 */
anthony6634c532012-06-15 06:56:39 +0000254 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000255 {
256 if (*p == '-')
257 flags^=XNegative; /* negate sign */
258 p++;
259 }
cristy3ed852e2009-09-05 21:47:34 +0000260 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000261 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000262 if (p != q)
cristy3ed852e2009-09-05 21:47:34 +0000263 {
anthony6634c532012-06-15 06:56:39 +0000264 flags|=XValue;
cristy57936e02012-07-03 00:33:28 +0000265 if ((flags & XNegative) != 0)
266 *x=(-*x);
cristy3ed852e2009-09-05 21:47:34 +0000267 }
268 }
anthony6634c532012-06-15 06:56:39 +0000269 if ((*p == '+') || (*p == '-'))
270 {
271 /*
272 Parse y value.
273 */
274 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000275 {
276 if (*p == '-')
277 flags^=YNegative; /* negate sign */
278 p++;
279 }
anthony6634c532012-06-15 06:56:39 +0000280 q=p;
281 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
282 if (p != q)
283 {
284 flags|=YValue;
cristy57936e02012-07-03 00:33:28 +0000285 if ((flags & YNegative) != 0)
286 *y=(-*y);
anthony6634c532012-06-15 06:56:39 +0000287 }
288 }
cristy64ad8f92012-08-03 00:58:24 +0000289 if ((flags & PercentValue) != 0)
cristy1ad6c872012-08-02 11:54:05 +0000290 {
cristy64ad8f92012-08-03 00:58:24 +0000291 if (((flags & SeparatorValue) == 0) && ((flags & HeightValue) == 0))
cristy243769e2012-08-12 14:39:15 +0000292 *height=(*width);
cristy64ad8f92012-08-03 00:58:24 +0000293 if (((flags & SeparatorValue) != 0) && ((flags & WidthValue) == 0))
294 *width=(*height);
cristy1ad6c872012-08-02 11:54:05 +0000295 }
anthony6634c532012-06-15 06:56:39 +0000296#if 0
297 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +0000298 (void) fprintf(stderr,"GetGeometry...\n");
299 (void) fprintf(stderr,"Input: %s\n",geometry);
300 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
301 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
302 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
303 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
304 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
305 *height,(long) *x,(long) *y);
anthony6634c532012-06-15 06:56:39 +0000306#endif
cristy3ed852e2009-09-05 21:47:34 +0000307 return(flags);
308}
309
310/*
311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312% %
313% %
314% %
315% G e t P a g e G e o m e t r y %
316% %
317% %
318% %
319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
320%
321% GetPageGeometry() replaces any page mneumonic with the equivalent size in
322% picas.
323%
324% The format of the GetPageGeometry method is:
325%
326% char *GetPageGeometry(const char *page_geometry)
327%
328% A description of each parameter follows.
329%
cristy272f23a2011-03-30 00:37:11 +0000330% o page_geometry: Specifies a pointer to an array of characters. The
331% string is either a Postscript page name (e.g. A4) or a postscript page
332% geometry (e.g. 612x792+36+36).
cristy3ed852e2009-09-05 21:47:34 +0000333%
334*/
335MagickExport char *GetPageGeometry(const char *page_geometry)
336{
337 static const char
338 *PageSizes[][2]=
339 {
340 { "4x6", "288x432" },
341 { "5x7", "360x504" },
342 { "7x9", "504x648" },
343 { "8x10", "576x720" },
344 { "9x11", "648x792" },
345 { "9x12", "648x864" },
346 { "10x13", "720x936" },
347 { "10x14", "720x1008" },
348 { "11x17", "792x1224" },
349 { "a0", "2384x3370" },
350 { "a1", "1684x2384" },
351 { "a10", "73x105" },
352 { "a2", "1191x1684" },
353 { "a3", "842x1191" },
354 { "a4", "595x842" },
355 { "a4smaLL", "595x842" },
356 { "a5", "420x595" },
357 { "a6", "297x420" },
358 { "a7", "210x297" },
359 { "a8", "148x210" },
360 { "a9", "105x148" },
361 { "archa", "648x864" },
362 { "archb", "864x1296" },
363 { "archC", "1296x1728" },
364 { "archd", "1728x2592" },
365 { "arche", "2592x3456" },
366 { "b0", "2920x4127" },
367 { "b1", "2064x2920" },
368 { "b10", "91x127" },
369 { "b2", "1460x2064" },
370 { "b3", "1032x1460" },
371 { "b4", "729x1032" },
372 { "b5", "516x729" },
373 { "b6", "363x516" },
374 { "b7", "258x363" },
375 { "b8", "181x258" },
376 { "b9", "127x181" },
377 { "c0", "2599x3676" },
378 { "c1", "1837x2599" },
379 { "c2", "1298x1837" },
380 { "c3", "918x1296" },
381 { "c4", "649x918" },
382 { "c5", "459x649" },
383 { "c6", "323x459" },
384 { "c7", "230x323" },
385 { "executive", "540x720" },
386 { "flsa", "612x936" },
387 { "flse", "612x936" },
388 { "folio", "612x936" },
389 { "halfletter", "396x612" },
390 { "isob0", "2835x4008" },
391 { "isob1", "2004x2835" },
392 { "isob10", "88x125" },
393 { "isob2", "1417x2004" },
394 { "isob3", "1001x1417" },
395 { "isob4", "709x1001" },
396 { "isob5", "499x709" },
397 { "isob6", "354x499" },
398 { "isob7", "249x354" },
399 { "isob8", "176x249" },
400 { "isob9", "125x176" },
401 { "jisb0", "1030x1456" },
402 { "jisb1", "728x1030" },
403 { "jisb2", "515x728" },
404 { "jisb3", "364x515" },
405 { "jisb4", "257x364" },
406 { "jisb5", "182x257" },
407 { "jisb6", "128x182" },
408 { "ledger", "1224x792" },
409 { "legal", "612x1008" },
410 { "letter", "612x792" },
411 { "lettersmaLL", "612x792" },
412 { "quarto", "610x780" },
413 { "statement", "396x612" },
414 { "tabloid", "792x1224" },
415 { (char *) NULL, (char *) NULL }
416 };
417
418 char
419 *page;
420
cristybb503372010-05-27 20:51:26 +0000421 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000422 i;
423
424 assert(page_geometry != (char *) NULL);
425 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
426 page=AcquireString(page_geometry);
427 for (i=0; *PageSizes[i] != (char *) NULL; i++)
428 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
429 {
430 RectangleInfo
431 geometry;
432
433 MagickStatusType
434 flags;
435
436 /*
437 Replace mneumonic with the equivalent size in dots-per-inch.
438 */
439 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
440 (void) ConcatenateMagickString(page,page_geometry+
441 strlen(PageSizes[i][0]),MaxTextExtent);
442 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
443 &geometry.height);
444 if ((flags & GreaterValue) == 0)
445 (void) ConcatenateMagickString(page,">",MaxTextExtent);
446 break;
447 }
448 return(page);
449}
450
451/*
452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
453% %
454% %
455% %
456% G r a v i t y A d j u s t G e o m e t r y %
457% %
cristy272f23a2011-03-30 00:37:11 +0000458% % % %
cristy3ed852e2009-09-05 21:47:34 +0000459%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
460%
461% GravityAdjustGeometry() adjusts the offset of a region with regard to the
462% given: width, height and gravity; against which it is positioned.
463%
464% The region should also have an appropriate width and height to correctly
465% set the right offset of the top left corner of the region.
466%
467% The format of the GravityAdjustGeometry method is:
468%
cristy272f23a2011-03-30 00:37:11 +0000469% void GravityAdjustGeometry(const size_t width, const size_t height,
470% const GravityType gravity,RectangleInfo *region);
cristy3ed852e2009-09-05 21:47:34 +0000471%
472% A description of each parameter follows:
473%
474% o width, height: the larger area the region is relative to
475%
476% o gravity: the edge/corner the current offset is relative to
477%
478% o region: The region requiring a offset adjustment relative to gravity
479%
480*/
cristybb503372010-05-27 20:51:26 +0000481MagickExport void GravityAdjustGeometry(const size_t width,
482 const size_t height,const GravityType gravity,RectangleInfo *region)
cristy3ed852e2009-09-05 21:47:34 +0000483{
484 if (region->height == 0)
485 region->height=height;
486 if (region->width == 0)
487 region->width=width;
488 switch (gravity)
489 {
490 case NorthEastGravity:
491 case EastGravity:
492 case SouthEastGravity:
493 {
cristybb503372010-05-27 20:51:26 +0000494 region->x=(ssize_t) (width-region->width-region->x);
cristy3ed852e2009-09-05 21:47:34 +0000495 break;
496 }
497 case NorthGravity:
498 case SouthGravity:
499 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000500 {
cristybb503372010-05-27 20:51:26 +0000501 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000502 break;
503 }
504 case ForgetGravity:
505 case NorthWestGravity:
506 case WestGravity:
507 case SouthWestGravity:
508 default:
509 break;
510 }
511 switch (gravity)
512 {
513 case SouthWestGravity:
514 case SouthGravity:
515 case SouthEastGravity:
516 {
cristybb503372010-05-27 20:51:26 +0000517 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000518 break;
519 }
520 case EastGravity:
521 case WestGravity:
522 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000523 {
cristybb503372010-05-27 20:51:26 +0000524 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000525 break;
526 }
527 case ForgetGravity:
528 case NorthWestGravity:
529 case NorthGravity:
530 case NorthEastGravity:
531 default:
532 break;
533 }
534 return;
535}
536
537/*
538%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
539% %
540% %
541% %
542+ I s G e o m e t r y %
543% %
544% %
545% %
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547%
548% IsGeometry() returns MagickTrue if the geometry specification is valid.
549% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
550%
551% The format of the IsGeometry method is:
552%
553% MagickBooleanType IsGeometry(const char *geometry)
554%
555% A description of each parameter follows:
556%
557% o geometry: This string is the geometry specification.
558%
559*/
560MagickExport MagickBooleanType IsGeometry(const char *geometry)
561{
562 GeometryInfo
563 geometry_info;
564
565 MagickStatusType
566 flags;
567
568 if (geometry == (const char *) NULL)
569 return(MagickFalse);
570 flags=ParseGeometry(geometry,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000571 return(flags != NoValue ? MagickTrue : MagickFalse);
572}
573
574/*
575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
576% %
577% %
578% %
579+ I s S c e n e G e o m e t r y %
580% %
581% %
582% %
583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584%
585% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
586% specification (e.g. [1], [1-9], [1,7,4]).
587%
588% The format of the IsSceneGeometry method is:
589%
590% MagickBooleanType IsSceneGeometry(const char *geometry,
591% const MagickBooleanType pedantic)
592%
593% A description of each parameter follows:
594%
595% o geometry: This string is the geometry specification.
596%
597% o pedantic: A value other than 0 invokes a more restrictive set of
598% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
599%
600*/
601MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
602 const MagickBooleanType pedantic)
603{
604 char
605 *p;
606
cristy00976d82011-02-20 20:31:28 +0000607 double
608 value;
609
cristy3ed852e2009-09-05 21:47:34 +0000610 if (geometry == (const char *) NULL)
611 return(MagickFalse);
612 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000613 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000614 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000615 if (p == geometry)
616 return(MagickFalse);
617 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
618 return(MagickFalse);
619 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
620 return(MagickFalse);
621 return(MagickTrue);
622}
623
624/*
625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
626% %
627% %
628% %
629% P a r s e A b s o l u t e G e o m e t r y %
630% %
631% %
632% %
633%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634%
anthonyd58e60c2011-03-30 00:24:34 +0000635% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
636% without any modification by percentages or gravity.
637%
638% It currently just a wrapper around GetGeometry(), but may be expanded in
639% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000640%
641% The format of the ParseAbsoluteGeometry method is:
642%
643% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
644% RectangleInfo *region_info)
645%
646% A description of each parameter follows:
647%
anthonyd58e60c2011-03-30 00:24:34 +0000648% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000649%
650% o region_info: the region as defined by the geometry string.
651%
652*/
653MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
654 RectangleInfo *region_info)
655{
656 MagickStatusType
657 flags;
658
659 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
660 &region_info->width,&region_info->height);
661 return(flags);
662}
663
664/*
665%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
666% %
667% %
668% %
669% P a r s e A f f i n e G e o m e t r y %
670% %
671% %
672% %
673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
674%
cristy272f23a2011-03-30 00:37:11 +0000675% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
676% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000677%
678% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000679%
680% The format of the ParseAffineGeometry method is:
681%
682% MagickStatusType ParseAffineGeometry(const char *geometry,
683% AffineMatrix *affine_matrix,ExceptionInfo *exception)
684%
685% A description of each parameter follows:
686%
anthonyd58e60c2011-03-30 00:24:34 +0000687% o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
cristy3ed852e2009-09-05 21:47:34 +0000688%
689% o affine_matrix: the affine matrix as defined by the geometry string.
690%
691% o exception: return any errors or warnings in this structure.
692%
693*/
694MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
695 AffineMatrix *affine_matrix,ExceptionInfo *exception)
696{
697 char
698 token[MaxTextExtent];
699
700 const char
701 *p;
702
703 double
704 determinant;
705
706 MagickStatusType
707 flags;
708
cristybb503372010-05-27 20:51:26 +0000709 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000710 i;
711
712 GetAffineMatrix(affine_matrix);
713 flags=NoValue;
714 p=(char *) geometry;
715 for (i=0; (*p != '\0') && (i < 6); i++)
716 {
717 GetMagickToken(p,&p,token);
718 if (*token == ',')
719 GetMagickToken(p,&p,token);
720 switch (i)
721 {
cristyc1acd842011-05-19 23:05:47 +0000722 case 0:
723 {
cristydbdd0e32011-11-04 23:29:40 +0000724 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000725 break;
726 }
727 case 1:
728 {
cristydbdd0e32011-11-04 23:29:40 +0000729 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000730 break;
731 }
732 case 2:
733 {
cristydbdd0e32011-11-04 23:29:40 +0000734 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000735 break;
736 }
737 case 3:
738 {
cristydbdd0e32011-11-04 23:29:40 +0000739 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000740 break;
741 }
742 case 4:
743 {
cristydbdd0e32011-11-04 23:29:40 +0000744 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000745 flags|=XValue;
746 break;
747 }
748 case 5:
749 {
cristydbdd0e32011-11-04 23:29:40 +0000750 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000751 flags|=YValue;
752 break;
753 }
cristy3ed852e2009-09-05 21:47:34 +0000754 }
755 }
cristy57936e02012-07-03 00:33:28 +0000756 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
757 affine_matrix->ry);
cristy3ed852e2009-09-05 21:47:34 +0000758 if (fabs(determinant) < MagickEpsilon)
759 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony92c93bd2012-03-19 14:02:47 +0000760 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
cristy3ed852e2009-09-05 21:47:34 +0000761 return(flags);
762}
763
764/*
765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
766% %
767% %
768% %
769% P a r s e G e o m e t r y %
770% %
771% %
772% %
773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
774%
775% ParseGeometry() parses a geometry specification and returns the sigma,
776% rho, xi, and psi values. It also returns flags that indicates which
777% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000778% whether the xi or pi values are negative.
779%
780% In addition, it reports if there are any of meta characters (%, !, <, >, @,
781% and ^) flags present. It does not report the location of the percentage
782% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000783%
anthony6634c532012-06-15 06:56:39 +0000784% Values may also be seperated by commas, colons, or slashes, and offsets.
785% Offsets may be prefixed by multiple signs to make offset string
786% substitutions easier to handle from shell scripts.
787% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
788% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
789% offsets.
790%
cristy3ed852e2009-09-05 21:47:34 +0000791% The format of the ParseGeometry method is:
792%
793% MagickStatusType ParseGeometry(const char *geometry,
794% GeometryInfo *geometry_info)
795%
796% A description of each parameter follows:
797%
anthonyd58e60c2011-03-30 00:24:34 +0000798% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000799%
800% o geometry_info: returns the parsed width/height/x/y in this structure.
801%
802*/
803MagickExport MagickStatusType ParseGeometry(const char *geometry,
804 GeometryInfo *geometry_info)
805{
806 char
807 *p,
808 pedantic_geometry[MaxTextExtent],
809 *q;
810
811 double
812 value;
813
cristyf30f47d2011-09-06 14:40:59 +0000814 int
815 c;
816
cristy3ed852e2009-09-05 21:47:34 +0000817 MagickStatusType
818 flags;
819
820 /*
821 Remove whitespaces meta characters from geometry specification.
822 */
823 assert(geometry_info != (GeometryInfo *) NULL);
824 flags=NoValue;
825 if ((geometry == (char *) NULL) || (*geometry == '\0'))
826 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000827 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000828 return(flags);
829 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
830 for (p=pedantic_geometry; *p != '\0'; )
831 {
832 if (isspace((int) ((unsigned char) *p)) != 0)
833 {
834 (void) CopyMagickString(p,p+1,MaxTextExtent);
835 continue;
836 }
cristy35abcb72011-09-06 20:36:15 +0000837 c=(int) ((unsigned char) *p);
838 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000839 {
840 case '%':
841 {
842 flags|=PercentValue;
843 (void) CopyMagickString(p,p+1,MaxTextExtent);
844 break;
845 }
846 case '!':
847 {
848 flags|=AspectValue;
849 (void) CopyMagickString(p,p+1,MaxTextExtent);
850 break;
851 }
852 case '<':
853 {
854 flags|=LessValue;
855 (void) CopyMagickString(p,p+1,MaxTextExtent);
856 break;
857 }
858 case '>':
859 {
860 flags|=GreaterValue;
861 (void) CopyMagickString(p,p+1,MaxTextExtent);
862 break;
863 }
864 case '^':
865 {
866 flags|=MinimumValue;
867 (void) CopyMagickString(p,p+1,MaxTextExtent);
868 break;
869 }
870 case '@':
871 {
872 flags|=AreaValue;
873 (void) CopyMagickString(p,p+1,MaxTextExtent);
874 break;
875 }
876 case '(':
877 case ')':
878 {
879 (void) CopyMagickString(p,p+1,MaxTextExtent);
880 break;
881 }
cristy1ad6c872012-08-02 11:54:05 +0000882 case 'x':
883 case 'X':
884 {
885 flags|=SeparatorValue;
886 p++;
887 break;
888 }
cristy3ed852e2009-09-05 21:47:34 +0000889 case '-':
890 case '+':
891 case ',':
892 case '0':
893 case '1':
894 case '2':
895 case '3':
896 case '4':
897 case '5':
898 case '6':
899 case '7':
900 case '8':
901 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000902 case '/':
903 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000904 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000905 {
906 p++;
907 break;
908 }
909 case '.':
910 {
911 p++;
912 flags|=DecimalValue;
913 break;
914 }
915 default:
916 return(flags);
917 }
918 }
919 /*
920 Parse rho, sigma, xi, psi, and optionally chi.
921 */
922 p=pedantic_geometry;
923 if (*p == '\0')
924 return(flags);
925 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000926 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000927 if (LocaleNCompare(p,"0x",2) == 0)
928 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000929 c=(int) ((unsigned char) *q);
930 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000931 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000932 {
933 /*
934 Parse rho.
935 */
936 q=p;
937 if (LocaleNCompare(p,"0x",2) == 0)
938 value=(double) strtol(p,&p,10);
939 else
cristydbdd0e32011-11-04 23:29:40 +0000940 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000941 if (p != q)
942 {
943 flags|=RhoValue;
944 geometry_info->rho=value;
945 }
946 }
947 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000948 c=(int) ((unsigned char) *p);
949 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000950 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000951 {
952 /*
953 Parse sigma.
954 */
955 p++;
956 while (isspace((int) ((unsigned char) *p)) != 0)
957 p++;
cristy35f75ac2011-09-06 20:33:35 +0000958 c=(int) ((unsigned char) *q);
959 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000960 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000961 {
962 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000963 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000964 if (p != q)
965 {
966 flags|=SigmaValue;
967 geometry_info->sigma=value;
968 }
969 }
970 }
971 while (isspace((int) ((unsigned char) *p)) != 0)
972 p++;
anthonyd1303502010-05-11 12:00:43 +0000973 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000974 {
975 /*
976 Parse xi value.
977 */
anthony6634c532012-06-15 06:56:39 +0000978 if ((*p == ',') || (*p == '/') || (*p == ':') )
cristy3ed852e2009-09-05 21:47:34 +0000979 p++;
anthony6634c532012-06-15 06:56:39 +0000980 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000981 {
982 if (*p == '-')
983 flags^=XiNegative; /* negate sign */
984 p++;
985 }
cristy3ed852e2009-09-05 21:47:34 +0000986 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000987 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000988 if (p != q)
989 {
990 flags|=XiValue;
cristy57936e02012-07-03 00:33:28 +0000991 if ((flags & XiNegative) != 0)
992 value=(-value);
cristy3ed852e2009-09-05 21:47:34 +0000993 geometry_info->xi=value;
994 }
995 while (isspace((int) ((unsigned char) *p)) != 0)
996 p++;
997 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000998 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000999 {
1000 /*
1001 Parse psi value.
1002 */
anthony6634c532012-06-15 06:56:39 +00001003 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001004 p++;
anthony6634c532012-06-15 06:56:39 +00001005 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +00001006 {
1007 if (*p == '-')
1008 flags^=PsiNegative; /* negate sign */
1009 p++;
1010 }
cristy3ed852e2009-09-05 21:47:34 +00001011 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001012 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +00001013 if (p != q)
1014 {
1015 flags|=PsiValue;
1016 if ((flags & PsiNegative) != 0)
1017 value=(-value);
1018 geometry_info->psi=value;
1019 }
1020 }
cristy3ed852e2009-09-05 21:47:34 +00001021 while (isspace((int) ((unsigned char) *p)) != 0)
1022 p++;
1023 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +00001024 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001025 {
1026 /*
1027 Parse chi value.
1028 */
anthony6634c532012-06-15 06:56:39 +00001029 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001030 p++;
anthony6634c532012-06-15 06:56:39 +00001031 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +00001032 {
1033 if (*p == '-')
1034 flags^=ChiNegative; /* negate sign */
1035 p++;
1036 }
cristy3ed852e2009-09-05 21:47:34 +00001037 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001038 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +00001039 if (p != q)
1040 {
1041 flags|=ChiValue;
1042 if ((flags & ChiNegative) != 0)
1043 value=(-value);
1044 geometry_info->chi=value;
1045 }
cristy3ed852e2009-09-05 21:47:34 +00001046 }
1047 }
1048 if (strchr(pedantic_geometry,':') != (char *) NULL)
1049 {
1050 /*
1051 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1052 */
1053 geometry_info->rho/=geometry_info->sigma;
1054 geometry_info->sigma=1.0;
1055 if (geometry_info->xi == 0.0)
1056 geometry_info->sigma=2.0;
1057 }
1058 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1059 ((flags & PsiValue) == 0))
1060 {
1061 /*
1062 Support negative height values (e.g. 30x-20).
1063 */
1064 geometry_info->sigma=geometry_info->xi;
1065 geometry_info->xi=0.0;
1066 flags|=SigmaValue;
1067 flags&=(~XiValue);
1068 }
cristy64ad8f92012-08-03 00:58:24 +00001069 if ((flags & PercentValue) != 0)
cristy1ad6c872012-08-02 11:54:05 +00001070 {
cristy64ad8f92012-08-03 00:58:24 +00001071 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
cristy243769e2012-08-12 14:39:15 +00001072 geometry_info->sigma=geometry_info->rho;
cristy64ad8f92012-08-03 00:58:24 +00001073 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1074 geometry_info->rho=geometry_info->sigma;
cristy1ad6c872012-08-02 11:54:05 +00001075 }
anthony6634c532012-06-15 06:56:39 +00001076#if 0
1077 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +00001078 (void) fprintf(stderr,"ParseGeometry...\n");
1079 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1080 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1081 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1082 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1083 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1084 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1085 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1086 geometry_info->chi);
anthony6634c532012-06-15 06:56:39 +00001087#endif
cristy3ed852e2009-09-05 21:47:34 +00001088 return(flags);
1089}
1090
1091/*
1092%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093% %
1094% %
1095% %
1096% P a r s e G r a v i t y G e o m e t r y %
1097% %
1098% %
1099% %
1100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1101%
1102% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001103% with respect to the given image page (canvas) dimensions and the images
1104% gravity setting.
1105%
1106% This is typically used for specifing a area within a given image for
1107% cropping images to a smaller size, chopping out rows and or columns, or
1108% resizing and positioning overlay images.
1109%
1110% Percentages are relative to image size and not page size, and are set to
1111% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001112%
1113% The format of the ParseGravityGeometry method is:
1114%
1115% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1116% RectangeInfo *region_info,ExceptionInfo *exception)
1117%
1118% A description of each parameter follows:
1119%
anthonyd58e60c2011-03-30 00:24:34 +00001120% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001121%
cristy272f23a2011-03-30 00:37:11 +00001122% o region_info: the region as defined by the geometry string with respect
1123% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001124%
1125% o exception: return any errors or warnings in this structure.
1126%
1127*/
1128MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1129 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1130{
1131 MagickStatusType
1132 flags;
1133
cristybb503372010-05-27 20:51:26 +00001134 size_t
cristy3ed852e2009-09-05 21:47:34 +00001135 height,
1136 width;
1137
1138 SetGeometry(image,region_info);
1139 if (image->page.width != 0)
1140 region_info->width=image->page.width;
1141 if (image->page.height != 0)
1142 region_info->height=image->page.height;
1143 flags=ParseAbsoluteGeometry(geometry,region_info);
1144 if (flags == NoValue)
1145 {
1146 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001147 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001148 return(flags);
1149 }
1150 if ((flags & PercentValue) != 0)
1151 {
1152 GeometryInfo
1153 geometry_info;
1154
1155 MagickStatusType
1156 status;
1157
1158 PointInfo
1159 scale;
1160
1161 /*
anthonyd58e60c2011-03-30 00:24:34 +00001162 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001163 */
1164 if (image->gravity != UndefinedGravity)
1165 flags|=XValue | YValue;
1166 status=ParseGeometry(geometry,&geometry_info);
1167 scale.x=geometry_info.rho;
1168 if ((status & RhoValue) == 0)
1169 scale.x=100.0;
1170 scale.y=geometry_info.sigma;
1171 if ((status & SigmaValue) == 0)
1172 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001173 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1174 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001175 }
1176 /*
1177 Adjust offset according to gravity setting.
1178 */
1179 width=region_info->width;
1180 height=region_info->height;
1181 if (width == 0)
1182 region_info->width=image->page.width | image->columns;
1183 if (height == 0)
1184 region_info->height=image->page.height | image->rows;
1185 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1186 region_info->width=width;
1187 region_info->height=height;
1188 return(flags);
1189}
1190
1191/*
1192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1193% %
1194% %
1195% %
1196+ P a r s e M e t a G e o m e t r y %
1197% %
1198% %
1199% %
1200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201%
1202% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001203% geometry is modified as determined by the meta characters: %, !, <, >, @,
1204% and ^ in relation to image resizing.
1205%
1206% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001207% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001208% image within the specified geometry width and height.
1209%
1210% Flags are interpreted...
anthony6634c532012-06-15 06:56:39 +00001211% % geometry size is given percentage of original width and height given
cristy272f23a2011-03-30 00:37:11 +00001212% ! do not try to preserve aspect ratio
1213% < only enlarge images smaller that geometry
1214% > only shrink images larger than geometry
1215% @ Fit image to contain at most this many pixels
1216% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001217%
1218% The format of the ParseMetaGeometry method is:
1219%
anthonyd58e60c2011-03-30 00:24:34 +00001220% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001221% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001222%
1223% A description of each parameter follows:
1224%
anthonyd58e60c2011-03-30 00:24:34 +00001225% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001226%
anthonyd58e60c2011-03-30 00:24:34 +00001227% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001228%
cristy272f23a2011-03-30 00:37:11 +00001229% o width,height: The width and height of original image, modified by
1230% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001231%
1232*/
1233
cristybb503372010-05-27 20:51:26 +00001234static inline size_t MagickMax(const size_t x,
1235 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001236{
1237 if (x > y)
1238 return(x);
1239 return(y);
1240}
1241
cristybb503372010-05-27 20:51:26 +00001242MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1243 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001244{
1245 GeometryInfo
1246 geometry_info;
1247
1248 MagickStatusType
1249 flags;
1250
cristybb503372010-05-27 20:51:26 +00001251 size_t
cristy3ed852e2009-09-05 21:47:34 +00001252 former_height,
1253 former_width;
1254
1255 /*
1256 Ensure the image geometry is valid.
1257 */
cristybb503372010-05-27 20:51:26 +00001258 assert(x != (ssize_t *) NULL);
1259 assert(y != (ssize_t *) NULL);
1260 assert(width != (size_t *) NULL);
1261 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001262 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1263 return(NoValue);
1264 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1265 /*
1266 Parse geometry using GetGeometry.
1267 */
1268 SetGeometryInfo(&geometry_info);
1269 former_width=(*width);
1270 former_height=(*height);
1271 flags=GetGeometry(geometry,x,y,width,height);
1272 if ((flags & PercentValue) != 0)
1273 {
1274 MagickStatusType
1275 flags;
1276
1277 PointInfo
1278 scale;
1279
1280 /*
1281 Geometry is a percentage of the image size.
1282 */
1283 flags=ParseGeometry(geometry,&geometry_info);
1284 scale.x=geometry_info.rho;
1285 if ((flags & RhoValue) == 0)
1286 scale.x=100.0;
1287 scale.y=geometry_info.sigma;
1288 if ((flags & SigmaValue) == 0)
1289 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001290 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristybb503372010-05-27 20:51:26 +00001291 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001292 former_width=(*width);
1293 former_height=(*height);
1294 }
cristy07623352011-12-07 13:04:03 +00001295 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1296 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001297 {
cristy07623352011-12-07 13:04:03 +00001298 if ((flags & RhoValue) == 0)
1299 *width=former_width;
1300 if ((flags & SigmaValue) == 0)
1301 *height=former_height;
1302 }
1303 else
1304 {
1305 double
cristy3ed852e2009-09-05 21:47:34 +00001306 scale_factor;
1307
1308 /*
1309 Respect aspect ratio of the image.
1310 */
1311 if ((former_width == 0) || (former_height == 0))
1312 scale_factor=1.0;
1313 else
1314 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1315 {
cristy07623352011-12-07 13:04:03 +00001316 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001317 if ((flags & MinimumValue) == 0)
1318 {
cristy07623352011-12-07 13:04:03 +00001319 if (scale_factor > ((double) *height/(double) former_height))
1320 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001321 }
1322 else
cristy07623352011-12-07 13:04:03 +00001323 if (scale_factor < ((double) *height/(double) former_height))
1324 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001325 }
1326 else
1327 if ((flags & RhoValue) != 0)
1328 {
cristy07623352011-12-07 13:04:03 +00001329 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001330 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001331 (scale_factor < ((double) *width/(double) former_height)))
1332 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001333 }
1334 else
1335 {
cristy07623352011-12-07 13:04:03 +00001336 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001337 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001338 (scale_factor < ((double) *height/(double) former_width)))
1339 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001340 }
cristy272f23a2011-03-30 00:37:11 +00001341 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1342 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001343 }
1344 if ((flags & GreaterValue) != 0)
1345 {
1346 if (former_width < *width)
1347 *width=former_width;
1348 if (former_height < *height)
1349 *height=former_height;
1350 }
1351 if ((flags & LessValue) != 0)
1352 {
1353 if (former_width > *width)
1354 *width=former_width;
1355 if (former_height > *height)
1356 *height=former_height;
1357 }
1358 if ((flags & AreaValue) != 0)
1359 {
cristy07623352011-12-07 13:04:03 +00001360 double
cristy3ed852e2009-09-05 21:47:34 +00001361 area,
1362 distance;
1363
1364 PointInfo
1365 scale;
1366
1367 /*
1368 Geometry is a maximum area in pixels.
1369 */
1370 (void) ParseGeometry(geometry,&geometry_info);
1371 area=geometry_info.rho;
1372 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001373 scale.x=(double) former_width/(distance/sqrt((double) area));
1374 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001375 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1376 {
cristy11e0a872012-01-10 16:10:21 +00001377 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1378 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001379 }
1380 former_width=(*width);
1381 former_height=(*height);
1382 }
1383 return(flags);
1384}
1385
1386/*
1387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388% %
1389% %
1390% %
1391% P a r s e P a g e G e o m e t r y %
1392% %
1393% %
1394% %
1395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1396%
1397% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001398% respect to the image page (canvas) dimensions.
1399%
1400% WARNING: Percentage dimensions remain relative to the actual image
1401% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001402%
1403% The format of the ParsePageGeometry method is:
1404%
1405% MagickStatusType ParsePageGeometry(const Image *image,
1406% const char *geometry,RectangeInfo *region_info,
1407% ExceptionInfo *exception)
1408%
1409% A description of each parameter follows:
1410%
anthonyd58e60c2011-03-30 00:24:34 +00001411% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001412%
1413% o region_info: the region as defined by the geometry string with
1414% respect to the image and its gravity.
1415%
1416% o exception: return any errors or warnings in this structure.
1417%
1418*/
1419MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1420 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1421{
1422 MagickStatusType
1423 flags;
1424
1425 SetGeometry(image,region_info);
1426 if (image->page.width != 0)
1427 region_info->width=image->page.width;
1428 if (image->page.height != 0)
1429 region_info->height=image->page.height;
1430 flags=ParseAbsoluteGeometry(geometry,region_info);
1431 if (flags == NoValue)
1432 {
1433 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001434 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001435 return(flags);
1436 }
1437 if ((flags & PercentValue) != 0)
1438 {
1439 region_info->width=image->columns;
1440 region_info->height=image->rows;
1441 }
1442 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1443 &region_info->width,&region_info->height);
cristy78afe012012-08-03 23:46:41 +00001444 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1445 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
cristy64ad8f92012-08-03 00:58:24 +00001446 {
1447 if ((flags & WidthValue) == 0)
1448 region_info->width=region_info->height;
1449 if ((flags & HeightValue) == 0)
1450 region_info->height=region_info->width;
1451 }
cristy3ed852e2009-09-05 21:47:34 +00001452 return(flags);
1453}
1454
1455/*
1456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457% %
1458% %
1459% %
1460% P a r s e R e g i o n G e o m e t r y %
1461% %
1462% %
1463% %
1464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465%
1466% ParseRegionGeometry() returns a region as defined by the geometry string
1467% with respect to the image dimensions and aspect ratio.
1468%
anthonyd58e60c2011-03-30 00:24:34 +00001469% This is basically a wrapper around ParseMetaGeometry. This is typically
1470% used to parse a geometry string to work out the final integer dimensions
1471% for image resizing.
1472%
cristy3ed852e2009-09-05 21:47:34 +00001473% The format of the ParseRegionGeometry method is:
1474%
1475% MagickStatusType ParseRegionGeometry(const Image *image,
1476% const char *geometry,RectangeInfo *region_info,
1477% ExceptionInfo *exception)
1478%
1479% A description of each parameter follows:
1480%
anthonyd58e60c2011-03-30 00:24:34 +00001481% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001482%
1483% o region_info: the region as defined by the geometry string.
1484%
1485% o exception: return any errors or warnings in this structure.
1486%
1487*/
1488MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1489 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1490{
1491 MagickStatusType
1492 flags;
1493
1494 SetGeometry(image,region_info);
1495 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1496 &region_info->width,&region_info->height);
1497 if (flags == NoValue)
1498 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001499 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001500 return(flags);
1501}
1502
1503/*
1504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1505% %
1506% %
1507% %
1508% S e t G e o m e t r y %
1509% %
1510% %
1511% %
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513%
1514% SetGeometry() sets the geometry to its default values.
1515%
1516% The format of the SetGeometry method is:
1517%
1518% SetGeometry(const Image *image,RectangleInfo *geometry)
1519%
1520% A description of each parameter follows:
1521%
1522% o image: the image.
1523%
1524% o geometry: the geometry.
1525%
1526*/
1527MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1528{
1529 assert(image != (Image *) NULL);
1530 assert(image->signature == MagickSignature);
1531 if (image->debug != MagickFalse)
1532 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1533 assert(geometry != (RectangleInfo *) NULL);
1534 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1535 geometry->width=image->columns;
1536 geometry->height=image->rows;
1537}
1538
1539/*
1540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1541% %
1542% %
1543% %
1544% S e t G e o m e t r y I n f o %
1545% %
1546% %
1547% %
1548%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1549%
1550% SetGeometryInfo sets the GeometryInfo structure to its default values.
1551%
1552% The format of the SetGeometryInfo method is:
1553%
1554% SetGeometryInfo(GeometryInfo *geometry_info)
1555%
1556% A description of each parameter follows:
1557%
1558% o geometry_info: the geometry info structure.
1559%
1560*/
1561MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1562{
1563 assert(geometry_info != (GeometryInfo *) NULL);
1564 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1565 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1566}