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