]> granicus.if.org Git - imagemagick/blob - magick/geometry.c
(no commit message)
[imagemagick] / magick / 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 %                               John Cristy                                   %
17 %                              January 2003                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    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 \f
39 /*
40   Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/constitute.h"
44 #include "magick/draw.h"
45 #include "magick/exception.h"
46 #include "magick/exception-private.h"
47 #include "magick/geometry.h"
48 #include "magick/memory_.h"
49 #include "magick/string_.h"
50 #include "magick/string-private.h"
51 #include "magick/token.h"
52 \f
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 %
70 %  The format of the GetGeometry method is:
71 %
72 %      MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
73 %        size_t *width,size_t *height)
74 %
75 %  A description of each parameter follows:
76 %
77 %    o geometry:  The geometry.
78 %
79 %    o x,y:  The x and y offset as determined by the geometry specification.
80 %
81 %    o width,height:  The width and height as determined by the geometry
82 %      specification.
83 %
84 */
85 MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
86   size_t *width,size_t *height)
87 {
88   char
89     *p,
90     pedantic_geometry[MaxTextExtent],
91     *q;
92
93   double
94     value;
95
96   MagickStatusType
97     flags;
98
99   /*
100     Remove whitespace and meta characters from geometry specification.
101   */
102   flags=NoValue;
103   if ((geometry == (char *) NULL) || (*geometry == '\0'))
104     return(flags);
105   if (strlen(geometry) >= MaxTextExtent)
106     return(flags);
107   (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
108   for (p=pedantic_geometry; *p != '\0'; )
109   {
110     if (isspace((int) ((unsigned char) *p)) != 0)
111       {
112         (void) CopyMagickString(p,p+1,MaxTextExtent);
113         continue;
114       }
115     switch ((int) *p)
116     {
117       case '%':
118       {
119         flags|=PercentValue;
120         (void) CopyMagickString(p,p+1,MaxTextExtent);
121         break;
122       }
123       case '!':
124       {
125         flags|=AspectValue;
126         (void) CopyMagickString(p,p+1,MaxTextExtent);
127         break;
128       }
129       case '<':
130       {
131         flags|=LessValue;
132         (void) CopyMagickString(p,p+1,MaxTextExtent);
133         break;
134       }
135       case '>':
136       {
137         flags|=GreaterValue;
138         (void) CopyMagickString(p,p+1,MaxTextExtent);
139         break;
140       }
141       case '^':
142       {
143         flags|=MinimumValue;
144         (void) CopyMagickString(p,p+1,MaxTextExtent);
145         break;
146       }
147       case '@':
148       {
149         flags|=AreaValue;
150         (void) CopyMagickString(p,p+1,MaxTextExtent);
151         break;
152       }
153       case '(':
154       case ')':
155       {
156         (void) CopyMagickString(p,p+1,MaxTextExtent);
157         break;
158       }
159       case '-':
160       case '.':
161       case ',':
162       case '+':
163       case '0':
164       case '1':
165       case '2':
166       case '3':
167       case '4':
168       case '5':
169       case '6':
170       case '7':
171       case '8':
172       case '9':
173       case -41:
174       case 'x':
175       case 'X':
176       {
177         p++;
178         break;
179       }
180       default:
181         return(flags);
182     }
183   }
184   /*
185     Parse width, height, x, and y.
186   */
187   p=pedantic_geometry;
188   if (*p == '\0')
189     return(flags);
190   q=p;
191   value=strtod(p,&q);
192   (void) value;
193   if (LocaleNCompare(p,"0x",2) == 0)
194     value=strtol(p,&q,10);
195   if ((((int) *q) == -41) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
196     {
197       /*
198         Parse width.
199       */
200       q=p;
201       if (LocaleNCompare(p,"0x",2) == 0)
202         *width=(size_t) strtol(p,&p,10);
203       else
204         *width=(size_t) floor(strtod(p,&p)+0.5);
205       if (p != q)
206         flags|=WidthValue;
207     }
208   if ((((int) *p) == -41) || (*p == 'x') || (*p == 'X'))
209     {
210       p++;
211       if ((*p != '+') && (*p != '-'))
212         {
213           /*
214             Parse height.
215           */
216           q=p;
217           *height=(size_t) floor(strtod(p,&p)+0.5);
218           if (p != q)
219             flags|=HeightValue;
220         }
221     }
222   if ((*p == '+') || (*p == '-'))
223     {
224       /*
225         Parse x value.
226       */
227       if (*p == '-')
228         flags|=XNegative;
229       q=p;
230       *x=(ssize_t) ceil(strtod(p,&p)-0.5);
231       if (p != q)
232         flags|=XValue;
233       if ((*p == '+') || (*p == '-'))
234         {
235           /*
236             Parse y value.
237           */
238           if (*p == '-')
239             flags|=YNegative;
240           q=p;
241           *y=(ssize_t) ceil(strtod(p,&p)-0.5);
242           if (p != q)
243             flags|=YValue;
244         }
245     }
246   return(flags);
247 }
248 \f
249 /*
250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251 %                                                                             %
252 %                                                                             %
253 %                                                                             %
254 %  G e t P a g e G e o m e t r y                                              %
255 %                                                                             %
256 %                                                                             %
257 %                                                                             %
258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259 %
260 %  GetPageGeometry() replaces any page mneumonic with the equivalent size in
261 %  picas.
262 %
263 %  The format of the GetPageGeometry method is:
264 %
265 %      char *GetPageGeometry(const char *page_geometry)
266 %
267 %  A description of each parameter follows.
268 %
269 %   o  page_geometry:  Specifies a pointer to an array of characters.
270 %      The string is either a Postscript page name (e.g. A4) or a postscript
271 %      page geometry (e.g. 612x792+36+36).
272 %
273 */
274 MagickExport char *GetPageGeometry(const char *page_geometry)
275 {
276   static const char
277     *PageSizes[][2]=
278     {
279       { "4x6",  "288x432" },
280       { "5x7",  "360x504" },
281       { "7x9",  "504x648" },
282       { "8x10", "576x720" },
283       { "9x11",  "648x792" },
284       { "9x12",  "648x864" },
285       { "10x13",  "720x936" },
286       { "10x14",  "720x1008" },
287       { "11x17",  "792x1224" },
288       { "a0",  "2384x3370" },
289       { "a1",  "1684x2384" },
290       { "a10", "73x105" },
291       { "a2",  "1191x1684" },
292       { "a3",  "842x1191" },
293       { "a4",  "595x842" },
294       { "a4smaLL", "595x842" },
295       { "a5",  "420x595" },
296       { "a6",  "297x420" },
297       { "a7",  "210x297" },
298       { "a8",  "148x210" },
299       { "a9",  "105x148" },
300       { "archa", "648x864" },
301       { "archb", "864x1296" },
302       { "archC", "1296x1728" },
303       { "archd", "1728x2592" },
304       { "arche", "2592x3456" },
305       { "b0",  "2920x4127" },
306       { "b1",  "2064x2920" },
307       { "b10", "91x127" },
308       { "b2",  "1460x2064" },
309       { "b3",  "1032x1460" },
310       { "b4",  "729x1032" },
311       { "b5",  "516x729" },
312       { "b6",  "363x516" },
313       { "b7",  "258x363" },
314       { "b8",  "181x258" },
315       { "b9",  "127x181" },
316       { "c0",  "2599x3676" },
317       { "c1",  "1837x2599" },
318       { "c2",  "1298x1837" },
319       { "c3",  "918x1296" },
320       { "c4",  "649x918" },
321       { "c5",  "459x649" },
322       { "c6",  "323x459" },
323       { "c7",  "230x323" },
324       { "executive", "540x720" },
325       { "flsa", "612x936" },
326       { "flse", "612x936" },
327       { "folio",  "612x936" },
328       { "halfletter", "396x612" },
329       { "isob0", "2835x4008" },
330       { "isob1", "2004x2835" },
331       { "isob10", "88x125" },
332       { "isob2", "1417x2004" },
333       { "isob3", "1001x1417" },
334       { "isob4", "709x1001" },
335       { "isob5", "499x709" },
336       { "isob6", "354x499" },
337       { "isob7", "249x354" },
338       { "isob8", "176x249" },
339       { "isob9", "125x176" },
340       { "jisb0", "1030x1456" },
341       { "jisb1", "728x1030" },
342       { "jisb2", "515x728" },
343       { "jisb3", "364x515" },
344       { "jisb4", "257x364" },
345       { "jisb5", "182x257" },
346       { "jisb6", "128x182" },
347       { "ledger",  "1224x792" },
348       { "legal",  "612x1008" },
349       { "letter", "612x792" },
350       { "lettersmaLL",  "612x792" },
351       { "quarto",  "610x780" },
352       { "statement",  "396x612" },
353       { "tabloid",  "792x1224" },
354       { (char *) NULL, (char *) NULL }
355     };
356
357   char
358     *page;
359
360   register ssize_t
361     i;
362
363   assert(page_geometry != (char *) NULL);
364   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
365   page=AcquireString(page_geometry);
366   for (i=0; *PageSizes[i] != (char *) NULL; i++)
367     if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
368       {
369         RectangleInfo
370           geometry;
371
372         MagickStatusType
373           flags;
374
375         /*
376           Replace mneumonic with the equivalent size in dots-per-inch.
377         */
378         (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
379         (void) ConcatenateMagickString(page,page_geometry+
380           strlen(PageSizes[i][0]),MaxTextExtent);
381         flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
382           &geometry.height);
383         if ((flags & GreaterValue) == 0)
384           (void) ConcatenateMagickString(page,">",MaxTextExtent);
385         break;
386       }
387   return(page);
388 }
389 \f
390 /*
391 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
392 %                                                                             %
393 %                                                                             %
394 %                                                                             %
395 %   G r a v i t y A d j u s t G e o m e t r y                                 %
396 %                                                                             %
397 %                                                                             %
398 %                                                                             %
399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400 %
401 %  GravityAdjustGeometry() adjusts the offset of a region with regard to the
402 %  given: width, height and gravity; against which it is positioned.
403 %
404 %  The region should also have an appropriate width and height to correctly
405 %  set the right offset of the top left corner of the region.
406 %
407 %  The format of the GravityAdjustGeometry method is:
408 %
409 %      void GravityAdjustGeometry(const size_t width,
410 %        const size_t height,const GravityType gravity,
411 %        RectangleInfo *region);
412 %
413 %  A description of each parameter follows:
414 %
415 %    o width, height:  the larger area the region is relative to
416 %
417 %    o gravity: the edge/corner the current offset is relative to
418 %
419 %    o region:  The region requiring a offset adjustment relative to gravity
420 %
421 */
422 MagickExport void GravityAdjustGeometry(const size_t width,
423   const size_t height,const GravityType gravity,RectangleInfo *region)
424 {
425   if (region->height == 0)
426     region->height=height;
427   if (region->width == 0)
428     region->width=width;
429   switch (gravity)
430   {
431     case NorthEastGravity:
432     case EastGravity:
433     case SouthEastGravity:
434     {
435       region->x=(ssize_t) (width-region->width-region->x);
436       break;
437     }
438     case NorthGravity:
439     case SouthGravity:
440     case CenterGravity:
441     case StaticGravity:
442     {
443       region->x+=(ssize_t) (width/2-region->width/2);
444       break;
445     }
446     case ForgetGravity:
447     case NorthWestGravity:
448     case WestGravity:
449     case SouthWestGravity:
450     default:
451       break;
452   }
453   switch (gravity)
454   {
455     case SouthWestGravity:
456     case SouthGravity:
457     case SouthEastGravity:
458     {
459       region->y=(ssize_t) (height-region->height-region->y);
460       break;
461     }
462     case EastGravity:
463     case WestGravity:
464     case CenterGravity:
465     case StaticGravity:
466     {
467       region->y+=(ssize_t) (height/2-region->height/2);
468       break;
469     }
470     case ForgetGravity:
471     case NorthWestGravity:
472     case NorthGravity:
473     case NorthEastGravity:
474     default:
475       break;
476   }
477   return;
478 }
479 \f
480 /*
481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482 %                                                                             %
483 %                                                                             %
484 %                                                                             %
485 +     I s G e o m e t r y                                                     %
486 %                                                                             %
487 %                                                                             %
488 %                                                                             %
489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490 %
491 %  IsGeometry() returns MagickTrue if the geometry specification is valid.
492 %  Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
493 %
494 %  The format of the IsGeometry method is:
495 %
496 %      MagickBooleanType IsGeometry(const char *geometry)
497 %
498 %  A description of each parameter follows:
499 %
500 %    o geometry: This string is the geometry specification.
501 %
502 */
503 MagickExport MagickBooleanType IsGeometry(const char *geometry)
504 {
505   GeometryInfo
506     geometry_info;
507
508   MagickStatusType
509     flags;
510
511   if (geometry == (const char *) NULL)
512     return(MagickFalse);
513   flags=ParseGeometry(geometry,&geometry_info);
514   if (flags == NoValue)
515     flags=ParseGeometry(geometry+1,&geometry_info);  /* i.e. +-4+-4 */
516   return(flags != NoValue ? MagickTrue : MagickFalse);
517 }
518 \f
519 /*
520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521 %                                                                             %
522 %                                                                             %
523 %                                                                             %
524 +     I s S c e n e G e o m e t r y                                           %
525 %                                                                             %
526 %                                                                             %
527 %                                                                             %
528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529 %
530 %  IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
531 %  specification (e.g. [1], [1-9], [1,7,4]).
532 %
533 %  The format of the IsSceneGeometry method is:
534 %
535 %      MagickBooleanType IsSceneGeometry(const char *geometry,
536 %        const MagickBooleanType pedantic)
537 %
538 %  A description of each parameter follows:
539 %
540 %    o geometry: This string is the geometry specification.
541 %
542 %    o pedantic: A value other than 0 invokes a more restrictive set of
543 %      conditions for a valid specification (e.g. [1], [1-4], [4-1]).
544 %
545 */
546 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
547   const MagickBooleanType pedantic)
548 {
549   char
550     *p;
551
552   double
553     value;
554
555   if (geometry == (const char *) NULL)
556     return(MagickFalse);
557   p=(char *) geometry;
558   value=strtod(geometry,&p);
559   (void) value;
560   if (p == geometry)
561     return(MagickFalse);
562   if (strspn(geometry,"0123456789-, ") != strlen(geometry))
563     return(MagickFalse);
564   if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
565     return(MagickFalse);
566   return(MagickTrue);
567 }
568 \f
569 /*
570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571 %                                                                             %
572 %                                                                             %
573 %                                                                             %
574 %   P a r s e A b s o l u t e G e o m e t r y                                 %
575 %                                                                             %
576 %                                                                             %
577 %                                                                             %
578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
579 %
580 %  ParseAbsoluteGeometry() returns a region as defined by the geometry string.
581 %
582 %  The format of the ParseAbsoluteGeometry method is:
583 %
584 %      MagickStatusType ParseAbsoluteGeometry(const char *geometry,
585 %        RectangleInfo *region_info)
586 %
587 %  A description of each parameter follows:
588 %
589 %    o geometry:  The geometry (e.g. 100x100+10+10).
590 %
591 %    o region_info: the region as defined by the geometry string.
592 %
593 */
594 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
595   RectangleInfo *region_info)
596 {
597   MagickStatusType
598     flags;
599
600   flags=GetGeometry(geometry,&region_info->x,&region_info->y,
601     &region_info->width,&region_info->height);
602   return(flags);
603 }
604 \f
605 /*
606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
607 %                                                                             %
608 %                                                                             %
609 %                                                                             %
610 %   P a r s e A f f i n e G e o m e t r y                                     %
611 %                                                                             %
612 %                                                                             %
613 %                                                                             %
614 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
615 %
616 %  ParseAffineGeometry() returns an affine matrix as defined by the geometry
617 %  string.
618 %
619 %  The format of the ParseAffineGeometry method is:
620 %
621 %      MagickStatusType ParseAffineGeometry(const char *geometry,
622 %        AffineMatrix *affine_matrix,ExceptionInfo *exception)
623 %
624 %  A description of each parameter follows:
625 %
626 %    o geometry:  The geometry (e.g. 1.0,0.0,0.0,1.0,3.2,1.2).
627 %
628 %    o affine_matrix: the affine matrix as defined by the geometry string.
629 %
630 %    o exception: return any errors or warnings in this structure.
631 %
632 */
633 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
634   AffineMatrix *affine_matrix,ExceptionInfo *exception)
635 {
636   char
637     token[MaxTextExtent];
638
639   const char
640     *p;
641
642   double
643     determinant;
644
645   MagickStatusType
646     flags;
647
648   register ssize_t
649     i;
650
651   GetAffineMatrix(affine_matrix);
652   flags=NoValue;
653   p=(char *) geometry;
654   for (i=0; (*p != '\0') && (i < 6); i++)
655   {
656     GetMagickToken(p,&p,token);
657     if (*token == ',')
658       GetMagickToken(p,&p,token);
659     switch (i)
660     {
661       case 0: affine_matrix->sx=StringToDouble(token); break;
662       case 1: affine_matrix->rx=StringToDouble(token); break;
663       case 2: affine_matrix->ry=StringToDouble(token); break;
664       case 3: affine_matrix->sy=StringToDouble(token); break;
665       case 4: affine_matrix->tx=StringToDouble(token); flags|=XValue; break;
666       case 5: affine_matrix->ty=StringToDouble(token); flags|=YValue; break;
667     }
668   }
669   determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
670     affine_matrix->ry);
671   if (fabs(determinant) < MagickEpsilon)
672     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
673       "InvalidGeometry","`%s'",geometry);
674   return(flags);
675 }
676 \f
677 /*
678 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
679 %                                                                             %
680 %                                                                             %
681 %                                                                             %
682 %   P a r s e G e o m e t r y                                                 %
683 %                                                                             %
684 %                                                                             %
685 %                                                                             %
686 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687 %
688 %  ParseGeometry() parses a geometry specification and returns the sigma,
689 %  rho, xi, and psi values.  It also returns flags that indicates which
690 %  of the four values (sigma, rho, xi, psi) were located in the string, and
691 %  whether the xi or pi values are negative.  In addition, there are flags to
692 %  report any meta characters (%, !, <, or >).
693 %
694 %  The format of the ParseGeometry method is:
695 %
696 %      MagickStatusType ParseGeometry(const char *geometry,
697 %        GeometryInfo *geometry_info)
698 %
699 %  A description of each parameter follows:
700 %
701 %    o geometry:  The geometry.
702 %
703 %    o geometry_info:  returns the parsed width/height/x/y in this structure.
704 %
705 */
706 MagickExport MagickStatusType ParseGeometry(const char *geometry,
707   GeometryInfo *geometry_info)
708 {
709   char
710     *p,
711     pedantic_geometry[MaxTextExtent],
712     *q;
713
714   double
715     value;
716
717   MagickStatusType
718     flags;
719
720   /*
721     Remove whitespaces meta characters from geometry specification.
722   */
723   assert(geometry_info != (GeometryInfo *) NULL);
724   flags=NoValue;
725   if ((geometry == (char *) NULL) || (*geometry == '\0'))
726     return(flags);
727   if (strlen(geometry) >= MaxTextExtent)
728     return(flags);
729   (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
730   for (p=pedantic_geometry; *p != '\0'; )
731   {
732     if (isspace((int) ((unsigned char) *p)) != 0)
733       {
734         (void) CopyMagickString(p,p+1,MaxTextExtent);
735         continue;
736       }
737     switch ((int) *p)
738     {
739       case '%':
740       {
741         flags|=PercentValue;
742         (void) CopyMagickString(p,p+1,MaxTextExtent);
743         break;
744       }
745       case '!':
746       {
747         flags|=AspectValue;
748         (void) CopyMagickString(p,p+1,MaxTextExtent);
749         break;
750       }
751       case '<':
752       {
753         flags|=LessValue;
754         (void) CopyMagickString(p,p+1,MaxTextExtent);
755         break;
756       }
757       case '>':
758       {
759         flags|=GreaterValue;
760         (void) CopyMagickString(p,p+1,MaxTextExtent);
761         break;
762       }
763       case '^':
764       {
765         flags|=MinimumValue;
766         (void) CopyMagickString(p,p+1,MaxTextExtent);
767         break;
768       }
769       case '@':
770       {
771         flags|=AreaValue;
772         (void) CopyMagickString(p,p+1,MaxTextExtent);
773         break;
774       }
775       case '(':
776       case ')':
777       {
778         (void) CopyMagickString(p,p+1,MaxTextExtent);
779         break;
780       }
781       case '-':
782       case '+':
783       case ',':
784       case '0':
785       case '1':
786       case '2':
787       case '3':
788       case '4':
789       case '5':
790       case '6':
791       case '7':
792       case '8':
793       case '9':
794       case -41:
795       case 'x':
796       case 'X':
797       case '/':
798       case ':':
799       {
800         p++;
801         break;
802       }
803       case '.':
804       {
805         p++;
806         flags|=DecimalValue;
807         break;
808       }
809       default:
810         return(flags);
811     }
812   }
813   /*
814     Parse rho, sigma, xi, psi, and optionally chi.
815   */
816   p=pedantic_geometry;
817   if (*p == '\0')
818     return(flags);
819   q=p;
820   value=strtod(p,&q);
821   if (LocaleNCompare(p,"0x",2) == 0)
822     value=(double) strtol(p,&q,10);
823   if ((((int) *q) == -41) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
824       (*q == '/') || (*q == ':') || (*q =='\0'))
825     {
826       /*
827         Parse rho.
828       */
829       q=p;
830       if (LocaleNCompare(p,"0x",2) == 0)
831         value=(double) strtol(p,&p,10);
832       else
833         value=strtod(p,&p);
834       if (p != q)
835         {
836           flags|=RhoValue;
837           geometry_info->rho=value;
838         }
839     }
840   q=p;
841   if ((((int) *p) == -41) || (*p == 'x') || (*p == 'X') || (*p == ',') ||
842       (*p == '/') || (*p == ':'))
843     {
844       /*
845         Parse sigma.
846       */
847       p++;
848       while (isspace((int) ((unsigned char) *p)) != 0)
849         p++;
850       if (((((int) *q) != -41) && (*q != 'x') && (*q != 'X')) ||
851           ((*p != '+') && (*p != '-')))
852         {
853           q=p;
854           value=strtod(p,&p);
855           if (p != q)
856             {
857               flags|=SigmaValue;
858               geometry_info->sigma=value;
859             }
860         }
861     }
862   while (isspace((int) ((unsigned char) *p)) != 0)
863     p++;
864   if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
865     {
866       /*
867         Parse xi value.
868       */
869       if ((*p == ',') || (*p == '/') || (*p == ':'))
870         p++;
871       q=p;
872       value=strtod(p,&p);
873       if (p != q)
874         {
875           flags|=XiValue;
876           if (*q == '-')
877             flags|=XiNegative;
878           geometry_info->xi=value;
879         }
880       while (isspace((int) ((unsigned char) *p)) != 0)
881         p++;
882       if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
883           (*p == ':'))
884         {
885           /*
886             Parse psi value.
887           */
888           if ((*p == ',') || (*p == '/') || (*p == ':'))
889             p++;
890           q=p;
891           value=strtod(p,&p);
892           if (p != q)
893             {
894               flags|=PsiValue;
895               if (*q == '-')
896                 flags|=PsiNegative;
897               geometry_info->psi=value;
898             }
899         }
900       while (isspace((int) ((unsigned char) *p)) != 0)
901         p++;
902       if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
903           (*p == ':'))
904         {
905           /*
906             Parse chi value.
907           */
908           if ((*p == ',') || (*p == '/') || (*p == ':'))
909             p++;
910           q=p;
911           value=strtod(p,&p);
912           if (p != q)
913             {
914               flags|=ChiValue;
915               if (*q == '-')
916                 flags|=ChiNegative;
917               geometry_info->chi=value;
918             }
919         }
920     }
921   if (strchr(pedantic_geometry,':') != (char *) NULL)
922     {
923       /*
924         Normalize sampling factor (e.g. 4:2:2 => 2x1).
925       */
926       geometry_info->rho/=geometry_info->sigma;
927       geometry_info->sigma=1.0;
928       if (geometry_info->xi == 0.0)
929         geometry_info->sigma=2.0;
930     }
931   if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
932       ((flags & PsiValue) == 0))
933     {
934       /*
935         Support negative height values (e.g. 30x-20).
936       */
937       geometry_info->sigma=geometry_info->xi;
938       geometry_info->xi=0.0;
939       flags|=SigmaValue;
940       flags&=(~XiValue);
941     }
942   return(flags);
943 }
944 \f
945 /*
946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 %                                                                             %
948 %                                                                             %
949 %                                                                             %
950 %   P a r s e G r a v i t y G e o m e t r y                                   %
951 %                                                                             %
952 %                                                                             %
953 %                                                                             %
954 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
955 %
956 %  ParseGravityGeometry() returns a region as defined by the geometry string
957 %  with respect to the image dimensions and its gravity.
958 %
959 %  The format of the ParseGravityGeometry method is:
960 %
961 %      MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
962 %        RectangeInfo *region_info,ExceptionInfo *exception)
963 %
964 %  A description of each parameter follows:
965 %
966 %    o geometry:  The geometry (e.g. 100x100+10+10).
967 %
968 %    o region_info: the region as defined by the geometry string with
969 %      respect to the image dimensions and its gravity.
970 %
971 %    o exception: return any errors or warnings in this structure.
972 %
973 */
974 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
975   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
976 {
977   MagickStatusType
978     flags;
979
980   size_t
981     height,
982     width;
983
984   SetGeometry(image,region_info);
985   if (image->page.width != 0)
986     region_info->width=image->page.width;
987   if (image->page.height != 0)
988     region_info->height=image->page.height;
989   flags=ParseAbsoluteGeometry(geometry,region_info);
990   if (flags == NoValue)
991     {
992       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
993         "InvalidGeometry","`%s'",geometry);
994       return(flags);
995     }
996   if ((flags & PercentValue) != 0)
997     {
998       GeometryInfo
999         geometry_info;
1000
1001       MagickStatusType
1002         status;
1003
1004       PointInfo
1005         scale;
1006
1007       /*
1008         Geometry is a percentage of the image size.
1009       */
1010       if (image->gravity != UndefinedGravity)
1011         flags|=XValue | YValue;
1012       status=ParseGeometry(geometry,&geometry_info);
1013       scale.x=geometry_info.rho;
1014       if ((status & RhoValue) == 0)
1015         scale.x=100.0;
1016       scale.y=geometry_info.sigma;
1017       if ((status & SigmaValue) == 0)
1018         scale.y=scale.x;
1019       region_info->width=(size_t) floor((scale.x*image->columns/100.0)+
1020         0.5);
1021       region_info->height=(size_t) floor((scale.y*image->rows/100.0)+
1022         0.5);
1023     }
1024   /*
1025     Adjust offset according to gravity setting.
1026   */
1027   width=region_info->width;
1028   height=region_info->height;
1029   if (width == 0)
1030     region_info->width=image->page.width | image->columns;
1031   if (height == 0)
1032     region_info->height=image->page.height | image->rows;
1033   GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1034   region_info->width=width;
1035   region_info->height=height;
1036   return(flags);
1037 }
1038 \f
1039 /*
1040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1041 %                                                                             %
1042 %                                                                             %
1043 %                                                                             %
1044 +   P a r s e M e t a G e o m e t r y                                         %
1045 %                                                                             %
1046 %                                                                             %
1047 %                                                                             %
1048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1049 %
1050 %  ParseMetaGeometry() is similar to GetGeometry() except the returned
1051 %  geometry is modified as determined by the meta characters:  %, !, <, >,
1052 %  and ~.
1053 %
1054 %  The format of the ParseMetaGeometry method is:
1055 %
1056 %      MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,ssize_t *y,
1057 %        size_t *width,size_t *height)
1058 %
1059 %  A description of each parameter follows:
1060 %
1061 %    o geometry:  The geometry.
1062 %
1063 %    o x,y:  The x and y offset as determined by the geometry specification.
1064 %
1065 %    o width,height:  The width and height as determined by the geometry
1066 %      specification.
1067 %
1068 */
1069
1070 static inline size_t MagickMax(const size_t x,
1071   const size_t y)
1072 {
1073   if (x > y)
1074     return(x);
1075   return(y);
1076 }
1077
1078 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1079   ssize_t *y,size_t *width,size_t *height)
1080 {
1081   GeometryInfo
1082     geometry_info;
1083
1084   MagickStatusType
1085     flags;
1086
1087   size_t
1088     former_height,
1089     former_width;
1090
1091   /*
1092     Ensure the image geometry is valid.
1093   */
1094   assert(x != (ssize_t *) NULL);
1095   assert(y != (ssize_t *) NULL);
1096   assert(width != (size_t *) NULL);
1097   assert(height != (size_t *) NULL);
1098   if ((geometry == (char *) NULL) || (*geometry == '\0'))
1099     return(NoValue);
1100   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1101   /*
1102     Parse geometry using GetGeometry.
1103   */
1104   SetGeometryInfo(&geometry_info);
1105   former_width=(*width);
1106   former_height=(*height);
1107   flags=GetGeometry(geometry,x,y,width,height);
1108   if ((flags & PercentValue) != 0)
1109     {
1110       MagickStatusType
1111         flags;
1112
1113       PointInfo
1114         scale;
1115
1116       /*
1117         Geometry is a percentage of the image size.
1118       */
1119       flags=ParseGeometry(geometry,&geometry_info);
1120       scale.x=geometry_info.rho;
1121       if ((flags & RhoValue) == 0)
1122         scale.x=100.0;
1123       scale.y=geometry_info.sigma;
1124       if ((flags & SigmaValue) == 0)
1125         scale.y=scale.x;
1126       *width=(size_t) floor(scale.x*former_width/100.0+0.5);
1127       if (*width == 0)
1128         *width=1;
1129       *height=(size_t) floor(scale.y*former_height/100.0+0.5);
1130       if (*height == 0)
1131         *height=1;
1132       former_width=(*width);
1133       former_height=(*height);
1134     }
1135   if (((flags & AspectValue) == 0) && ((*width != former_width) ||
1136       (*height != former_height)))
1137     {
1138       MagickRealType
1139         scale_factor;
1140
1141       /*
1142         Respect aspect ratio of the image.
1143       */
1144       if ((former_width == 0) || (former_height == 0))
1145         scale_factor=1.0;
1146       else
1147         if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1148           {
1149             scale_factor=(MagickRealType) *width/(MagickRealType) former_width;
1150             if ((flags & MinimumValue) == 0)
1151               {
1152                 if (scale_factor > ((MagickRealType) *height/
1153                     (MagickRealType) former_height))
1154                   scale_factor=(MagickRealType) *height/(MagickRealType)
1155                     former_height;
1156               }
1157             else
1158               if (scale_factor < ((MagickRealType) *height/
1159                   (MagickRealType) former_height))
1160                 scale_factor=(MagickRealType) *height/(MagickRealType)
1161                   former_height;
1162           }
1163         else
1164           if ((flags & RhoValue) != 0)
1165             {
1166               scale_factor=(MagickRealType) *width/(MagickRealType)
1167                 former_width;
1168               if (((flags & MinimumValue) != 0) &&
1169                   (scale_factor < ((MagickRealType) *width/
1170                     (MagickRealType) former_height)))
1171                 scale_factor=(MagickRealType) *width/(MagickRealType)
1172                   former_height;
1173             }
1174           else
1175             {
1176               scale_factor=(MagickRealType) *height/(MagickRealType)
1177                 former_height;
1178               if (((flags & MinimumValue) != 0) &&
1179                   (scale_factor < ((MagickRealType) *height/
1180                     (MagickRealType) former_width)))
1181                 scale_factor=(MagickRealType) *height/(MagickRealType)
1182                   former_width;
1183             }
1184       *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),
1185         1UL);
1186       *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),
1187         1UL);
1188     }
1189   if ((flags & GreaterValue) != 0)
1190     {
1191       if (former_width < *width)
1192         *width=former_width;
1193       if (former_height < *height)
1194         *height=former_height;
1195     }
1196   if ((flags & LessValue) != 0)
1197     {
1198       if (former_width > *width)
1199         *width=former_width;
1200       if (former_height > *height)
1201         *height=former_height;
1202     }
1203   if ((flags & AreaValue) != 0)
1204     {
1205       MagickRealType
1206         area,
1207         distance;
1208
1209       PointInfo
1210         scale;
1211
1212       /*
1213         Geometry is a maximum area in pixels.
1214       */
1215       (void) ParseGeometry(geometry,&geometry_info);
1216       area=geometry_info.rho;
1217       distance=sqrt((double) former_width*former_height);
1218       scale.x=(double) former_width/(double) (distance/sqrt((double) area));
1219       scale.y=(double) former_height/(double) (distance/sqrt((double) area));
1220       if ((scale.x < (double) *width) || (scale.y < (double) *height))
1221         {
1222           *width=(size_t) (former_width/(distance/sqrt((double) area)));
1223           *height=(size_t) (former_height/(distance/
1224             sqrt((double) area)));
1225         }
1226       former_width=(*width);
1227       former_height=(*height);
1228     }
1229   return(flags);
1230 }
1231 \f
1232 /*
1233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1234 %                                                                             %
1235 %                                                                             %
1236 %                                                                             %
1237 %   P a r s e P a g e G e o m e t r y                                         %
1238 %                                                                             %
1239 %                                                                             %
1240 %                                                                             %
1241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242 %
1243 %  ParsePageGeometry() returns a region as defined by the geometry string with
1244 %  respect to the image dimensions.
1245 %
1246 %  The format of the ParsePageGeometry method is:
1247 %
1248 %      MagickStatusType ParsePageGeometry(const Image *image,
1249 %        const char *geometry,RectangeInfo *region_info,
1250 %        ExceptionInfo *exception)
1251 %
1252 %  A description of each parameter follows:
1253 %
1254 %    o geometry:  The geometry (e.g. 100x100+10+10).
1255 %
1256 %    o region_info: the region as defined by the geometry string with
1257 %      respect to the image and its gravity.
1258 %
1259 %    o exception: return any errors or warnings in this structure.
1260 %
1261 */
1262 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1263   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1264 {
1265   MagickStatusType
1266     flags;
1267
1268   SetGeometry(image,region_info);
1269   if (image->page.width != 0)
1270     region_info->width=image->page.width;
1271   if (image->page.height != 0)
1272     region_info->height=image->page.height;
1273   flags=ParseAbsoluteGeometry(geometry,region_info);
1274   if (flags == NoValue)
1275     {
1276       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1277         "InvalidGeometry","`%s'",geometry);
1278       return(flags);
1279     }
1280   if ((flags & PercentValue) != 0)
1281     {
1282       region_info->width=image->columns;
1283       region_info->height=image->rows;
1284     }
1285   flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1286     &region_info->width,&region_info->height);
1287   return(flags);
1288 }
1289 \f
1290 /*
1291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1292 %                                                                             %
1293 %                                                                             %
1294 %                                                                             %
1295 %   P a r s e R e g i o n G e o m e t r y                                     %
1296 %                                                                             %
1297 %                                                                             %
1298 %                                                                             %
1299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1300 %
1301 %  ParseRegionGeometry() returns a region as defined by the geometry string
1302 %  with respect to the image dimensions and aspect ratio.
1303 %
1304 %  The format of the ParseRegionGeometry method is:
1305 %
1306 %      MagickStatusType ParseRegionGeometry(const Image *image,
1307 %        const char *geometry,RectangeInfo *region_info,
1308 %        ExceptionInfo *exception)
1309 %
1310 %  A description of each parameter follows:
1311 %
1312 %    o geometry:  The geometry (e.g. 100x100+10+10).
1313 %
1314 %    o region_info: the region as defined by the geometry string.
1315 %
1316 %    o exception: return any errors or warnings in this structure.
1317 %
1318 */
1319 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1320   const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1321 {
1322   MagickStatusType
1323     flags;
1324
1325   SetGeometry(image,region_info);
1326   flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1327     &region_info->width,&region_info->height);
1328   if (flags == NoValue)
1329     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1330       "InvalidGeometry","`%s'",geometry);
1331   return(flags);
1332 }
1333 \f
1334 /*
1335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1336 %                                                                             %
1337 %                                                                             %
1338 %                                                                             %
1339 %   S e t G e o m e t r y                                                     %
1340 %                                                                             %
1341 %                                                                             %
1342 %                                                                             %
1343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1344 %
1345 %  SetGeometry() sets the geometry to its default values.
1346 %
1347 %  The format of the SetGeometry method is:
1348 %
1349 %      SetGeometry(const Image *image,RectangleInfo *geometry)
1350 %
1351 %  A description of each parameter follows:
1352 %
1353 %    o image: the image.
1354 %
1355 %    o geometry: the geometry.
1356 %
1357 */
1358 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1359 {
1360   assert(image != (Image *) NULL);
1361   assert(image->signature == MagickSignature);
1362   if (image->debug != MagickFalse)
1363     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1364   assert(geometry != (RectangleInfo *) NULL);
1365   (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1366   geometry->width=image->columns;
1367   geometry->height=image->rows;
1368 }
1369 \f
1370 /*
1371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1372 %                                                                             %
1373 %                                                                             %
1374 %                                                                             %
1375 %   S e t G e o m e t r y I n f o                                             %
1376 %                                                                             %
1377 %                                                                             %
1378 %                                                                             %
1379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1380 %
1381 %  SetGeometryInfo sets the GeometryInfo structure to its default values.
1382 %
1383 %  The format of the SetGeometryInfo method is:
1384 %
1385 %      SetGeometryInfo(GeometryInfo *geometry_info)
1386 %
1387 %  A description of each parameter follows:
1388 %
1389 %    o geometry_info: the geometry info structure.
1390 %
1391 */
1392 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1393 {
1394   assert(geometry_info != (GeometryInfo *) NULL);
1395   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1396   (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1397 }