blob: 6ff1bba557f8f9f3eafb6fe0d5c6cd5278f4f926 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% GGGG EEEEE OOO M M EEEEE TTTTT RRRR Y Y %
7% G E O O MM MM E T R R Y Y %
8% G GG EEE O O M M M EEE T RRRR Y %
9% G G E O O M M E T R R Y %
10% GGGG EEEEE OOO M M EEEEE T R R Y %
11% %
12% %
13% MagickCore Geometry Methods %
14% %
15% Software Design %
16% John Cristy %
17% January 2003 %
18% %
19% %
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
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
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*/
84MagickExport 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
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*/
272MagickExport 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
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*/
420MagickExport 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
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*/
501MagickExport 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
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*/
544MagickExport 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
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*/
591MagickExport 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
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*/
630MagickExport 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
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*/
703MagickExport 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
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*/
971MagickExport 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
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
1065static 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
1073MagickExport 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
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*/
1255MagickExport 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
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*/
1312MagickExport 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
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*/
1351MagickExport 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
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*/
1385MagickExport 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}