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