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