1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % SSSSS V V GGGG % 7 % SS V V G % 8 % SSS V V G GG % 9 % SS V V G G % 10 % SSSSS V GGG % 11 % % 12 % % 13 % Read/Write Scalable Vector Graphics Format % 14 % % 15 % Software Design % 16 % Cristy % 17 % William Radcliffe % 18 % March 2000 % 19 % % 20 % % 21 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 22 % dedicated to making software imaging solutions freely available. % 23 % % 24 % You may not use this file except in compliance with the License. You may % 25 % obtain a copy of the License at % 26 % % 27 % http://www.imagemagick.org/script/license.php % 28 % % 29 % Unless required by applicable law or agreed to in writing, software % 30 % distributed under the License is distributed on an "AS IS" BASIS, % 31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 32 % See the License for the specific language governing permissions and % 33 % limitations under the License. % 34 % % 35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 36 % 37 % 38 */ 39 40 /* 42 Include declarations. 43 */ 44 #include "MagickCore/studio.h" 45 #include "MagickCore/annotate.h" 46 #include "MagickCore/artifact.h" 47 #include "MagickCore/attribute.h" 48 #include "MagickCore/blob.h" 49 #include "MagickCore/blob-private.h" 50 #include "MagickCore/cache.h" 51 #include "MagickCore/constitute.h" 52 #include "MagickCore/composite-private.h" 53 #include "MagickCore/delegate.h" 54 #include "MagickCore/delegate-private.h" 55 #include "MagickCore/draw.h" 56 #include "MagickCore/exception.h" 57 #include "MagickCore/exception-private.h" 58 #include "MagickCore/gem.h" 59 #include "MagickCore/image.h" 60 #include "MagickCore/image-private.h" 61 #include "MagickCore/list.h" 62 #include "MagickCore/log.h" 63 #include "MagickCore/magick.h" 64 #include "MagickCore/memory_.h" 65 #include "MagickCore/module.h" 66 #include "MagickCore/monitor.h" 67 #include "MagickCore/monitor-private.h" 68 #include "MagickCore/quantum-private.h" 69 #include "MagickCore/pixel-accessor.h" 70 #include "MagickCore/property.h" 71 #include "MagickCore/resource_.h" 72 #include "MagickCore/static.h" 73 #include "MagickCore/string_.h" 74 #include "MagickCore/string-private.h" 75 #include "MagickCore/token.h" 76 #include "MagickCore/utility.h" 77 #if defined(MAGICKCORE_XML_DELEGATE) 78 # if defined(MAGICKCORE_WINDOWS_SUPPORT) 79 # if !defined(__MINGW32__) && !defined(__MINGW64__) 80 # include <win32config.h> 81 # endif 82 # endif 83 # include <libxml/parser.h> 84 # include <libxml/xmlmemory.h> 85 # include <libxml/parserInternals.h> 86 # include <libxml/xmlerror.h> 87 #endif 88 89 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE) 90 #include "autotrace/autotrace.h" 91 #endif 92 93 #if defined(MAGICKCORE_RSVG_DELEGATE) 94 #include "librsvg/rsvg.h" 95 #if !defined(LIBRSVG_CHECK_VERSION) 96 #include "librsvg/rsvg-cairo.h" 97 #include "librsvg/librsvg-features.h" 98 #elif !LIBRSVG_CHECK_VERSION(2,36,2) 99 #include "librsvg/rsvg-cairo.h" 100 #include "librsvg/librsvg-features.h" 101 #endif 102 #endif 103 104 /* 106 Typedef declarations. 107 */ 108 typedef struct _BoundingBox 109 { 110 double 111 x, 112 y, 113 width, 114 height; 115 } BoundingBox; 116 117 typedef struct _ElementInfo 118 { 119 double 120 cx, 121 cy, 122 major, 123 minor, 124 angle; 125 } ElementInfo; 126 127 typedef struct _SVGInfo 128 { 129 FILE 130 *file; 131 132 ExceptionInfo 133 *exception; 134 135 Image 136 *image; 137 138 const ImageInfo 139 *image_info; 140 141 AffineMatrix 142 affine; 143 144 size_t 145 width, 146 height; 147 148 char 149 *size, 150 *title, 151 *comment; 152 153 int 154 n; 155 156 double 157 *scale, 158 pointsize; 159 160 ElementInfo 161 element; 162 163 SegmentInfo 164 segment; 165 166 BoundingBox 167 bounds, 168 center, 169 view_box; 170 171 PointInfo 172 radius; 173 174 char 175 *stop_color, 176 *offset, 177 *text, 178 *vertices, 179 *url; 180 181 #if defined(MAGICKCORE_XML_DELEGATE) 182 xmlParserCtxtPtr 183 parser; 184 185 xmlDocPtr 186 document; 187 #endif 188 } SVGInfo; 189 190 /* 192 Forward declarations. 193 */ 194 static MagickBooleanType 195 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *); 196 197 /* 199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 200 % % 201 % % 202 % % 203 % I s S V G % 204 % % 205 % % 206 % % 207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 208 % 209 % IsSVG()() returns MagickTrue if the image format type, identified by the 210 % magick string, is SVG. 211 % 212 % The format of the IsSVG method is: 213 % 214 % MagickBooleanType IsSVG(const unsigned char *magick,const size_t length) 215 % 216 % A description of each parameter follows: 217 % 218 % o magick: compare image format pattern against these bytes. 219 % 220 % o length: Specifies the length of the magick string. 221 % 222 */ 223 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length) 224 { 225 if (length < 4) 226 return(MagickFalse); 227 if (LocaleNCompare((const char *) magick,"?xml",4) == 0) 228 return(MagickTrue); 229 return(MagickFalse); 230 } 231 232 #if defined(MAGICKCORE_XML_DELEGATE) 233 /* 234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 235 % % 236 % % 237 % % 238 % R e a d S V G I m a g e % 239 % % 240 % % 241 % % 242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 243 % 244 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It 245 % allocates the memory necessary for the new Image structure and returns a 246 % pointer to the new image. 247 % 248 % The format of the ReadSVGImage method is: 249 % 250 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception) 251 % 252 % A description of each parameter follows: 253 % 254 % o image_info: the image info. 255 % 256 % o exception: return any errors or warnings in this structure. 257 % 258 */ 259 260 static SVGInfo *AcquireSVGInfo(void) 261 { 262 SVGInfo 263 *svg_info; 264 265 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info)); 266 if (svg_info == (SVGInfo *) NULL) 267 return((SVGInfo *) NULL); 268 (void) ResetMagickMemory(svg_info,0,sizeof(*svg_info)); 269 svg_info->text=AcquireString(""); 270 svg_info->scale=(double *) AcquireMagickMemory(sizeof(*svg_info->scale)); 271 if (svg_info->scale == (double *) NULL) 272 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 273 GetAffineMatrix(&svg_info->affine); 274 svg_info->scale[0]=ExpandAffine(&svg_info->affine); 275 return(svg_info); 276 } 277 278 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info) 279 { 280 if (svg_info->text != (char *) NULL) 281 svg_info->text=DestroyString(svg_info->text); 282 if (svg_info->scale != (double *) NULL) 283 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale); 284 if (svg_info->title != (char *) NULL) 285 svg_info->title=DestroyString(svg_info->title); 286 if (svg_info->comment != (char *) NULL) 287 svg_info->comment=DestroyString(svg_info->comment); 288 return((SVGInfo *) RelinquishMagickMemory(svg_info)); 289 } 290 291 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type, 292 const char *string) 293 { 294 char 295 *next_token, 296 token[MagickPathExtent]; 297 298 const char 299 *p; 300 301 double 302 value; 303 304 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string); 305 assert(string != (const char *) NULL); 306 p=(const char *) string; 307 GetNextToken(p,&p,MagickPathExtent,token); 308 value=StringToDouble(token,&next_token); 309 if (strchr(token,'%') != (char *) NULL) 310 { 311 double 312 alpha, 313 beta; 314 315 if (type > 0) 316 { 317 if (svg_info->view_box.width == 0.0) 318 return(0.0); 319 return(svg_info->view_box.width*value/100.0); 320 } 321 if (type < 0) 322 { 323 if (svg_info->view_box.height == 0.0) 324 return(0.0); 325 return(svg_info->view_box.height*value/100.0); 326 } 327 alpha=value-svg_info->view_box.width; 328 beta=value-svg_info->view_box.height; 329 return(hypot(alpha,beta)/sqrt(2.0)/100.0); 330 } 331 GetNextToken(p,&p,MagickPathExtent,token); 332 if (LocaleNCompare(token,"cm",2) == 0) 333 return(DefaultResolution*svg_info->scale[0]/2.54*value); 334 if (LocaleNCompare(token,"em",2) == 0) 335 return(svg_info->pointsize*value); 336 if (LocaleNCompare(token,"ex",2) == 0) 337 return(svg_info->pointsize*value/2.0); 338 if (LocaleNCompare(token,"in",2) == 0) 339 return(DefaultResolution*svg_info->scale[0]*value); 340 if (LocaleNCompare(token,"mm",2) == 0) 341 return(DefaultResolution*svg_info->scale[0]/25.4*value); 342 if (LocaleNCompare(token,"pc",2) == 0) 343 return(DefaultResolution*svg_info->scale[0]/6.0*value); 344 if (LocaleNCompare(token,"pt",2) == 0) 345 return(1.25*svg_info->scale[0]*value); 346 if (LocaleNCompare(token,"px",2) == 0) 347 return(value); 348 return(value); 349 } 350 351 static void StripStyleTokens(char *message) 352 { 353 register char 354 *p, 355 *q; 356 357 size_t 358 length; 359 360 assert(message != (char *) NULL); 361 if (*message == '\0') 362 return; 363 length=strlen(message); 364 p=message; 365 while (isspace((int) ((unsigned char) *p)) != 0) 366 p++; 367 q=message+length-1; 368 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p)) 369 q--; 370 (void) CopyMagickMemory(message,p,(size_t) (q-p+1)); 371 message[q-p+1]='\0'; 372 StripString(message); 373 } 374 375 static char **GetStyleTokens(void *context,const char *style,int *number_tokens) 376 { 377 char 378 *text, 379 **tokens; 380 381 register ssize_t 382 i; 383 384 SVGInfo 385 *svg_info; 386 387 svg_info=(SVGInfo *) context; 388 (void) svg_info; 389 *number_tokens=0; 390 if (style == (const char *) NULL) 391 return((char **) NULL); 392 text=AcquireString(style); 393 (void) SubstituteString(&text,":","\n"); 394 (void) SubstituteString(&text,";","\n"); 395 tokens=StringToList(text); 396 text=DestroyString(text); 397 for (i=0; tokens[i] != (char *) NULL; i++) 398 StripStyleTokens(tokens[i]); 399 *number_tokens=(int) i; 400 return(tokens); 401 } 402 403 static char **GetTransformTokens(void *context,const char *text, 404 int *number_tokens) 405 { 406 char 407 **tokens; 408 409 register const char 410 *p, 411 *q; 412 413 register size_t 414 i; 415 416 size_t 417 extent; 418 419 SVGInfo 420 *svg_info; 421 422 svg_info=(SVGInfo *) context; 423 *number_tokens=0; 424 if (text == (const char *) NULL) 425 return((char **) NULL); 426 extent=8; 427 tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens)); 428 if (tokens == (char **) NULL) 429 { 430 (void) ThrowMagickException(svg_info->exception,GetMagickModule(), 431 ResourceLimitError,"MemoryAllocationFailed","`%s'",text); 432 return((char **) NULL); 433 } 434 /* 435 Convert string to an ASCII list. 436 */ 437 i=0; 438 p=text; 439 for (q=p; *q != '\0'; q++) 440 { 441 if ((*q != '(') && (*q != ')') && (*q != '\0')) 442 continue; 443 if (i == extent) 444 { 445 extent<<=1; 446 tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens)); 447 if (tokens == (char **) NULL) 448 { 449 (void) ThrowMagickException(svg_info->exception,GetMagickModule(), 450 ResourceLimitError,"MemoryAllocationFailed","`%s'",text); 451 return((char **) NULL); 452 } 453 } 454 tokens[i]=AcquireString(p); 455 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1)); 456 StripString(tokens[i]); 457 i++; 458 p=q+1; 459 } 460 tokens[i]=AcquireString(p); 461 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1)); 462 StripString(tokens[i++]); 463 tokens[i]=(char *) NULL; 464 *number_tokens=i; 465 return(tokens); 466 } 467 468 #if defined(__cplusplus) || defined(c_plusplus) 469 extern "C" { 470 #endif 471 472 static int SVGIsStandalone(void *context) 473 { 474 SVGInfo 475 *svg_info; 476 477 /* 478 Is this document tagged standalone? 479 */ 480 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()"); 481 svg_info=(SVGInfo *) context; 482 return(svg_info->document->standalone == 1); 483 } 484 485 static int SVGHasInternalSubset(void *context) 486 { 487 SVGInfo 488 *svg_info; 489 490 /* 491 Does this document has an internal subset? 492 */ 493 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 494 " SAX.SVGHasInternalSubset()"); 495 svg_info=(SVGInfo *) context; 496 return(svg_info->document->intSubset != NULL); 497 } 498 499 static int SVGHasExternalSubset(void *context) 500 { 501 SVGInfo 502 *svg_info; 503 504 /* 505 Does this document has an external subset? 506 */ 507 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 508 " SAX.SVGHasExternalSubset()"); 509 svg_info=(SVGInfo *) context; 510 return(svg_info->document->extSubset != NULL); 511 } 512 513 static void SVGInternalSubset(void *context,const xmlChar *name, 514 const xmlChar *external_id,const xmlChar *system_id) 515 { 516 SVGInfo 517 *svg_info; 518 519 /* 520 Does this document has an internal subset? 521 */ 522 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 523 " SAX.internalSubset(%s, %s, %s)",(const char *) name, 524 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"), 525 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none")); 526 svg_info=(SVGInfo *) context; 527 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id); 528 } 529 530 static xmlParserInputPtr SVGResolveEntity(void *context, 531 const xmlChar *public_id,const xmlChar *system_id) 532 { 533 SVGInfo 534 *svg_info; 535 536 xmlParserInputPtr 537 stream; 538 539 /* 540 Special entity resolver, better left to the parser, it has more 541 context than the application layer. The default behaviour is to 542 not resolve the entities, in that case the ENTITY_REF nodes are 543 built in the structure (and the parameter values). 544 */ 545 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 546 " SAX.resolveEntity(%s, %s)", 547 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"), 548 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none")); 549 svg_info=(SVGInfo *) context; 550 stream=xmlLoadExternalEntity((const char *) system_id,(const char *) 551 public_id,svg_info->parser); 552 return(stream); 553 } 554 555 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name) 556 { 557 SVGInfo 558 *svg_info; 559 560 /* 561 Get an entity by name. 562 */ 563 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)", 564 name); 565 svg_info=(SVGInfo *) context; 566 return(xmlGetDocEntity(svg_info->document,name)); 567 } 568 569 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name) 570 { 571 SVGInfo 572 *svg_info; 573 574 /* 575 Get a parameter entity by name. 576 */ 577 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 578 " SAX.getParameterEntity(%s)",name); 579 svg_info=(SVGInfo *) context; 580 return(xmlGetParameterEntity(svg_info->document,name)); 581 } 582 583 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type, 584 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content) 585 { 586 SVGInfo 587 *svg_info; 588 589 /* 590 An entity definition has been parsed. 591 */ 592 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 593 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type, 594 public_id != (xmlChar *) NULL ? (const char *) public_id : "none", 595 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content); 596 svg_info=(SVGInfo *) context; 597 if (svg_info->parser->inSubset == 1) 598 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id, 599 content); 600 else 601 if (svg_info->parser->inSubset == 2) 602 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id, 603 content); 604 } 605 606 static void SVGAttributeDeclaration(void *context,const xmlChar *element, 607 const xmlChar *name,int type,int value,const xmlChar *default_value, 608 xmlEnumerationPtr tree) 609 { 610 SVGInfo 611 *svg_info; 612 613 xmlChar 614 *fullname, 615 *prefix; 616 617 xmlParserCtxtPtr 618 parser; 619 620 /* 621 An attribute definition has been parsed. 622 */ 623 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 624 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value, 625 default_value); 626 svg_info=(SVGInfo *) context; 627 fullname=(xmlChar *) NULL; 628 prefix=(xmlChar *) NULL; 629 parser=svg_info->parser; 630 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix); 631 if (parser->inSubset == 1) 632 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset, 633 element,fullname,prefix,(xmlAttributeType) type, 634 (xmlAttributeDefault) value,default_value,tree); 635 else 636 if (parser->inSubset == 2) 637 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset, 638 element,fullname,prefix,(xmlAttributeType) type, 639 (xmlAttributeDefault) value,default_value,tree); 640 if (prefix != (xmlChar *) NULL) 641 xmlFree(prefix); 642 if (fullname != (xmlChar *) NULL) 643 xmlFree(fullname); 644 } 645 646 static void SVGElementDeclaration(void *context,const xmlChar *name,int type, 647 xmlElementContentPtr content) 648 { 649 SVGInfo 650 *svg_info; 651 652 xmlParserCtxtPtr 653 parser; 654 655 /* 656 An element definition has been parsed. 657 */ 658 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 659 " SAX.elementDecl(%s, %d, ...)",name,type); 660 svg_info=(SVGInfo *) context; 661 parser=svg_info->parser; 662 if (parser->inSubset == 1) 663 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset, 664 name,(xmlElementTypeVal) type,content); 665 else 666 if (parser->inSubset == 2) 667 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset, 668 name,(xmlElementTypeVal) type,content); 669 } 670 671 static void SVGNotationDeclaration(void *context,const xmlChar *name, 672 const xmlChar *public_id,const xmlChar *system_id) 673 { 674 SVGInfo 675 *svg_info; 676 677 xmlParserCtxtPtr 678 parser; 679 680 /* 681 What to do when a notation declaration has been parsed. 682 */ 683 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 684 " SAX.notationDecl(%s, %s, %s)",name, 685 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none", 686 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"); 687 svg_info=(SVGInfo *) context; 688 parser=svg_info->parser; 689 if (parser->inSubset == 1) 690 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset, 691 name,public_id,system_id); 692 else 693 if (parser->inSubset == 2) 694 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset, 695 name,public_id,system_id); 696 } 697 698 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name, 699 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation) 700 { 701 SVGInfo 702 *svg_info; 703 704 /* 705 What to do when an unparsed entity declaration is parsed. 706 */ 707 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 708 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name, 709 public_id != (xmlChar *) NULL ? (const char *) public_id : "none", 710 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation); 711 svg_info=(SVGInfo *) context; 712 (void) xmlAddDocEntity(svg_info->document,name, 713 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation); 714 715 } 716 717 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location) 718 { 719 SVGInfo 720 *svg_info; 721 722 /* 723 Receive the document locator at startup, actually xmlDefaultSAXLocator. 724 */ 725 (void) location; 726 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 727 " SAX.setDocumentLocator()"); 728 svg_info=(SVGInfo *) context; 729 (void) svg_info; 730 } 731 732 static void SVGStartDocument(void *context) 733 { 734 SVGInfo 735 *svg_info; 736 737 xmlParserCtxtPtr 738 parser; 739 740 /* 741 Called when the document start being processed. 742 */ 743 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()"); 744 svg_info=(SVGInfo *) context; 745 parser=svg_info->parser; 746 svg_info->document=xmlNewDoc(parser->version); 747 if (svg_info->document == (xmlDocPtr) NULL) 748 return; 749 if (parser->encoding == NULL) 750 svg_info->document->encoding=(const xmlChar *) NULL; 751 else 752 svg_info->document->encoding=xmlStrdup(parser->encoding); 753 svg_info->document->standalone=parser->standalone; 754 } 755 756 static void SVGEndDocument(void *context) 757 { 758 SVGInfo 759 *svg_info; 760 761 /* 762 Called when the document end has been detected. 763 */ 764 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()"); 765 svg_info=(SVGInfo *) context; 766 if (svg_info->offset != (char *) NULL) 767 svg_info->offset=DestroyString(svg_info->offset); 768 if (svg_info->stop_color != (char *) NULL) 769 svg_info->stop_color=DestroyString(svg_info->stop_color); 770 if (svg_info->scale != (double *) NULL) 771 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale); 772 if (svg_info->text != (char *) NULL) 773 svg_info->text=DestroyString(svg_info->text); 774 if (svg_info->vertices != (char *) NULL) 775 svg_info->vertices=DestroyString(svg_info->vertices); 776 if (svg_info->url != (char *) NULL) 777 svg_info->url=DestroyString(svg_info->url); 778 #if defined(MAGICKCORE_XML_DELEGATE) 779 if (svg_info->document != (xmlDocPtr) NULL) 780 { 781 xmlFreeDoc(svg_info->document); 782 svg_info->document=(xmlDocPtr) NULL; 783 } 784 #endif 785 } 786 787 static void SVGStartElement(void *context,const xmlChar *name, 788 const xmlChar **attributes) 789 { 790 char 791 *color, 792 id[MagickPathExtent], 793 *next_token, 794 token[MagickPathExtent], 795 **tokens, 796 *units; 797 798 const char 799 *keyword, 800 *p, 801 *value; 802 803 int 804 number_tokens; 805 806 SVGInfo 807 *svg_info; 808 809 register ssize_t 810 i, 811 j; 812 813 /* 814 Called when an opening tag has been processed. 815 */ 816 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s", 817 name); 818 svg_info=(SVGInfo *) context; 819 svg_info->n++; 820 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale, 821 svg_info->n+1UL,sizeof(*svg_info->scale)); 822 if (svg_info->scale == (double *) NULL) 823 { 824 (void) ThrowMagickException(svg_info->exception,GetMagickModule(), 825 ResourceLimitError,"MemoryAllocationFailed","`%s'",name); 826 return; 827 } 828 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1]; 829 color=AcquireString("none"); 830 units=AcquireString("userSpaceOnUse"); 831 *id='\0'; 832 *token='\0'; 833 value=(const char *) NULL; 834 if (attributes != (const xmlChar **) NULL) 835 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2) 836 { 837 keyword=(const char *) attributes[i]; 838 value=(const char *) attributes[i+1]; 839 switch (*keyword) 840 { 841 case 'C': 842 case 'c': 843 { 844 if (LocaleCompare(keyword,"cx") == 0) 845 { 846 svg_info->element.cx= 847 GetUserSpaceCoordinateValue(svg_info,1,value); 848 break; 849 } 850 if (LocaleCompare(keyword,"cy") == 0) 851 { 852 svg_info->element.cy= 853 GetUserSpaceCoordinateValue(svg_info,-1,value); 854 break; 855 } 856 break; 857 } 858 case 'F': 859 case 'f': 860 { 861 if (LocaleCompare(keyword,"fx") == 0) 862 { 863 svg_info->element.major= 864 GetUserSpaceCoordinateValue(svg_info,1,value); 865 break; 866 } 867 if (LocaleCompare(keyword,"fy") == 0) 868 { 869 svg_info->element.minor= 870 GetUserSpaceCoordinateValue(svg_info,-1,value); 871 break; 872 } 873 break; 874 } 875 case 'H': 876 case 'h': 877 { 878 if (LocaleCompare(keyword,"height") == 0) 879 { 880 svg_info->bounds.height= 881 GetUserSpaceCoordinateValue(svg_info,-1,value); 882 break; 883 } 884 break; 885 } 886 case 'I': 887 case 'i': 888 { 889 if (LocaleCompare(keyword,"id") == 0) 890 { 891 (void) CopyMagickString(id,value,MagickPathExtent); 892 break; 893 } 894 break; 895 } 896 case 'R': 897 case 'r': 898 { 899 if (LocaleCompare(keyword,"r") == 0) 900 { 901 svg_info->element.angle= 902 GetUserSpaceCoordinateValue(svg_info,0,value); 903 break; 904 } 905 break; 906 } 907 case 'W': 908 case 'w': 909 { 910 if (LocaleCompare(keyword,"width") == 0) 911 { 912 svg_info->bounds.width= 913 GetUserSpaceCoordinateValue(svg_info,1,value); 914 break; 915 } 916 break; 917 } 918 case 'X': 919 case 'x': 920 { 921 if (LocaleCompare(keyword,"x") == 0) 922 { 923 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value)- 924 svg_info->center.x; 925 break; 926 } 927 if (LocaleCompare(keyword,"x1") == 0) 928 { 929 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1, 930 value); 931 break; 932 } 933 if (LocaleCompare(keyword,"x2") == 0) 934 { 935 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1, 936 value); 937 break; 938 } 939 break; 940 } 941 case 'Y': 942 case 'y': 943 { 944 if (LocaleCompare(keyword,"y") == 0) 945 { 946 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value)- 947 svg_info->center.y; 948 break; 949 } 950 if (LocaleCompare(keyword,"y1") == 0) 951 { 952 svg_info->segment.y1= 953 GetUserSpaceCoordinateValue(svg_info,-1,value); 954 break; 955 } 956 if (LocaleCompare(keyword,"y2") == 0) 957 { 958 svg_info->segment.y2= 959 GetUserSpaceCoordinateValue(svg_info,-1,value); 960 break; 961 } 962 break; 963 } 964 default: 965 break; 966 } 967 } 968 if (strchr((char *) name,':') != (char *) NULL) 969 { 970 /* 971 Skip over namespace. 972 */ 973 for ( ; *name != ':'; name++) ; 974 name++; 975 } 976 switch (*name) 977 { 978 case 'C': 979 case 'c': 980 { 981 if (LocaleCompare((const char *) name,"circle") == 0) 982 { 983 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 984 break; 985 } 986 if (LocaleCompare((const char *) name,"clipPath") == 0) 987 { 988 (void) FormatLocaleFile(svg_info->file,"push clip-path '%s'\n",id); 989 break; 990 } 991 break; 992 } 993 case 'D': 994 case 'd': 995 { 996 if (LocaleCompare((const char *) name,"defs") == 0) 997 { 998 (void) FormatLocaleFile(svg_info->file,"push defs\n"); 999 break; 1000 } 1001 break; 1002 } 1003 case 'E': 1004 case 'e': 1005 { 1006 if (LocaleCompare((const char *) name,"ellipse") == 0) 1007 { 1008 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1009 break; 1010 } 1011 break; 1012 } 1013 case 'G': 1014 case 'g': 1015 { 1016 if (LocaleCompare((const char *) name,"g") == 0) 1017 { 1018 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1019 break; 1020 } 1021 break; 1022 } 1023 case 'I': 1024 case 'i': 1025 { 1026 if (LocaleCompare((const char *) name,"image") == 0) 1027 { 1028 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1029 break; 1030 } 1031 break; 1032 } 1033 case 'L': 1034 case 'l': 1035 { 1036 if (LocaleCompare((const char *) name,"line") == 0) 1037 { 1038 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1039 break; 1040 } 1041 if (LocaleCompare((const char *) name,"linearGradient") == 0) 1042 { 1043 (void) FormatLocaleFile(svg_info->file, 1044 "push gradient '%s' linear %g,%g %g,%g\n",id, 1045 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2, 1046 svg_info->segment.y2); 1047 break; 1048 } 1049 break; 1050 } 1051 case 'P': 1052 case 'p': 1053 { 1054 if (LocaleCompare((const char *) name,"path") == 0) 1055 { 1056 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1057 break; 1058 } 1059 if (LocaleCompare((const char *) name,"pattern") == 0) 1060 { 1061 (void) FormatLocaleFile(svg_info->file, 1062 "push pattern '%s' %g,%g %g,%g\n",id, 1063 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width, 1064 svg_info->bounds.height); 1065 break; 1066 } 1067 if (LocaleCompare((const char *) name,"polygon") == 0) 1068 { 1069 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1070 break; 1071 } 1072 if (LocaleCompare((const char *) name,"polyline") == 0) 1073 { 1074 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1075 break; 1076 } 1077 break; 1078 } 1079 case 'R': 1080 case 'r': 1081 { 1082 if (LocaleCompare((const char *) name,"radialGradient") == 0) 1083 { 1084 (void) FormatLocaleFile(svg_info->file, 1085 "push gradient '%s' radial %g,%g %g,%g %g\n", 1086 id,svg_info->element.cx,svg_info->element.cy, 1087 svg_info->element.major,svg_info->element.minor, 1088 svg_info->element.angle); 1089 break; 1090 } 1091 if (LocaleCompare((const char *) name,"rect") == 0) 1092 { 1093 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1094 break; 1095 } 1096 break; 1097 } 1098 case 'S': 1099 case 's': 1100 { 1101 if (LocaleCompare((const char *) name,"svg") == 0) 1102 { 1103 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1104 break; 1105 } 1106 break; 1107 } 1108 case 'T': 1109 case 't': 1110 { 1111 if (LocaleCompare((const char *) name,"text") == 0) 1112 { 1113 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1114 svg_info->bounds.x=0.0; 1115 svg_info->bounds.y=0.0; 1116 svg_info->bounds.width=0.0; 1117 svg_info->bounds.height=0.0; 1118 break; 1119 } 1120 if (LocaleCompare((const char *) name,"tspan") == 0) 1121 { 1122 if (*svg_info->text != '\0') 1123 { 1124 DrawInfo 1125 *draw_info; 1126 1127 TypeMetric 1128 metrics; 1129 1130 char 1131 *text; 1132 1133 text=EscapeString(svg_info->text,'\''); 1134 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n", 1135 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y- 1136 svg_info->center.y,text); 1137 text=DestroyString(text); 1138 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL); 1139 draw_info->pointsize=svg_info->pointsize; 1140 draw_info->text=AcquireString(svg_info->text); 1141 (void) ConcatenateString(&draw_info->text," "); 1142 (void) GetTypeMetrics(svg_info->image,draw_info, 1143 &metrics,svg_info->exception); 1144 svg_info->bounds.x+=metrics.width; 1145 draw_info=DestroyDrawInfo(draw_info); 1146 *svg_info->text='\0'; 1147 } 1148 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); 1149 break; 1150 } 1151 break; 1152 } 1153 default: 1154 break; 1155 } 1156 if (attributes != (const xmlChar **) NULL) 1157 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2) 1158 { 1159 keyword=(const char *) attributes[i]; 1160 value=(const char *) attributes[i+1]; 1161 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1162 " %s = %s",keyword,value); 1163 switch (*keyword) 1164 { 1165 case 'A': 1166 case 'a': 1167 { 1168 if (LocaleCompare(keyword,"angle") == 0) 1169 { 1170 (void) FormatLocaleFile(svg_info->file,"angle %g\n", 1171 GetUserSpaceCoordinateValue(svg_info,0,value)); 1172 break; 1173 } 1174 break; 1175 } 1176 case 'C': 1177 case 'c': 1178 { 1179 if (LocaleCompare(keyword,"clip-path") == 0) 1180 { 1181 (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value); 1182 break; 1183 } 1184 if (LocaleCompare(keyword,"clip-rule") == 0) 1185 { 1186 (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value); 1187 break; 1188 } 1189 if (LocaleCompare(keyword,"clipPathUnits") == 0) 1190 { 1191 (void) CloneString(&units,value); 1192 (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value); 1193 break; 1194 } 1195 if (LocaleCompare(keyword,"color") == 0) 1196 { 1197 (void) CloneString(&color,value); 1198 break; 1199 } 1200 if (LocaleCompare(keyword,"cx") == 0) 1201 { 1202 svg_info->element.cx= 1203 GetUserSpaceCoordinateValue(svg_info,1,value); 1204 break; 1205 } 1206 if (LocaleCompare(keyword,"cy") == 0) 1207 { 1208 svg_info->element.cy= 1209 GetUserSpaceCoordinateValue(svg_info,-1,value); 1210 break; 1211 } 1212 break; 1213 } 1214 case 'D': 1215 case 'd': 1216 { 1217 if (LocaleCompare(keyword,"d") == 0) 1218 { 1219 (void) CloneString(&svg_info->vertices,value); 1220 break; 1221 } 1222 if (LocaleCompare(keyword,"dx") == 0) 1223 { 1224 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value); 1225 break; 1226 } 1227 if (LocaleCompare(keyword,"dy") == 0) 1228 { 1229 svg_info->bounds.y+= 1230 GetUserSpaceCoordinateValue(svg_info,-1,value); 1231 break; 1232 } 1233 break; 1234 } 1235 case 'F': 1236 case 'f': 1237 { 1238 if (LocaleCompare(keyword,"fill") == 0) 1239 { 1240 if (LocaleCompare(value,"currentColor") == 0) 1241 { 1242 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color); 1243 break; 1244 } 1245 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value); 1246 break; 1247 } 1248 if (LocaleCompare(keyword,"fillcolor") == 0) 1249 { 1250 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value); 1251 break; 1252 } 1253 if (LocaleCompare(keyword,"fill-rule") == 0) 1254 { 1255 (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value); 1256 break; 1257 } 1258 if (LocaleCompare(keyword,"fill-alpha") == 0) 1259 { 1260 (void) FormatLocaleFile(svg_info->file,"fill-alpha '%s'\n", 1261 value); 1262 break; 1263 } 1264 if (LocaleCompare(keyword,"font-family") == 0) 1265 { 1266 (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n", 1267 value); 1268 break; 1269 } 1270 if (LocaleCompare(keyword,"font-stretch") == 0) 1271 { 1272 (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n", 1273 value); 1274 break; 1275 } 1276 if (LocaleCompare(keyword,"font-style") == 0) 1277 { 1278 (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value); 1279 break; 1280 } 1281 if (LocaleCompare(keyword,"font-size") == 0) 1282 { 1283 if (LocaleCompare(value,"xx-small") == 0) 1284 svg_info->pointsize=6.144; 1285 else if (LocaleCompare(value,"x-small") == 0) 1286 svg_info->pointsize=7.68; 1287 else if (LocaleCompare(value,"small") == 0) 1288 svg_info->pointsize=9.6; 1289 else if (LocaleCompare(value,"medium") == 0) 1290 svg_info->pointsize=12.0; 1291 else if (LocaleCompare(value,"large") == 0) 1292 svg_info->pointsize=14.4; 1293 else if (LocaleCompare(value,"x-large") == 0) 1294 svg_info->pointsize=17.28; 1295 else if (LocaleCompare(value,"xx-large") == 0) 1296 svg_info->pointsize=20.736; 1297 else 1298 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0, 1299 value); 1300 (void) FormatLocaleFile(svg_info->file,"font-size %g\n", 1301 svg_info->pointsize); 1302 break; 1303 } 1304 if (LocaleCompare(keyword,"font-weight") == 0) 1305 { 1306 (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n", 1307 value); 1308 break; 1309 } 1310 break; 1311 } 1312 case 'G': 1313 case 'g': 1314 { 1315 if (LocaleCompare(keyword,"gradientTransform") == 0) 1316 { 1317 AffineMatrix 1318 affine, 1319 current, 1320 transform; 1321 1322 GetAffineMatrix(&transform); 1323 (void) LogMagickEvent(CoderEvent,GetMagickModule()," "); 1324 tokens=GetTransformTokens(context,value,&number_tokens); 1325 if (tokens == (char **) NULL) 1326 break; 1327 for (j=0; j < (number_tokens-1); j+=2) 1328 { 1329 keyword=(char *) tokens[j]; 1330 if (keyword == (char *) NULL) 1331 continue; 1332 value=(char *) tokens[j+1]; 1333 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1334 " %s: %s",keyword,value); 1335 current=transform; 1336 GetAffineMatrix(&affine); 1337 switch (*keyword) 1338 { 1339 case 'M': 1340 case 'm': 1341 { 1342 if (LocaleCompare(keyword,"matrix") == 0) 1343 { 1344 p=(const char *) value; 1345 GetNextToken(p,&p,MagickPathExtent,token); 1346 affine.sx=StringToDouble(value,(char **) NULL); 1347 GetNextToken(p,&p,MagickPathExtent,token); 1348 if (*token == ',') 1349 GetNextToken(p,&p,MagickPathExtent,token); 1350 affine.rx=StringToDouble(token,&next_token); 1351 GetNextToken(p,&p,MagickPathExtent,token); 1352 if (*token == ',') 1353 GetNextToken(p,&p,MagickPathExtent,token); 1354 affine.ry=StringToDouble(token,&next_token); 1355 GetNextToken(p,&p,MagickPathExtent,token); 1356 if (*token == ',') 1357 GetNextToken(p,&p,MagickPathExtent,token); 1358 affine.sy=StringToDouble(token,&next_token); 1359 GetNextToken(p,&p,MagickPathExtent,token); 1360 if (*token == ',') 1361 GetNextToken(p,&p,MagickPathExtent,token); 1362 affine.tx=StringToDouble(token,&next_token); 1363 GetNextToken(p,&p,MagickPathExtent,token); 1364 if (*token == ',') 1365 GetNextToken(p,&p,MagickPathExtent,token); 1366 affine.ty=StringToDouble(token,&next_token); 1367 break; 1368 } 1369 break; 1370 } 1371 case 'R': 1372 case 'r': 1373 { 1374 if (LocaleCompare(keyword,"rotate") == 0) 1375 { 1376 double 1377 angle; 1378 1379 angle=GetUserSpaceCoordinateValue(svg_info,0,value); 1380 affine.sx=cos(DegreesToRadians(fmod(angle,360.0))); 1381 affine.rx=sin(DegreesToRadians(fmod(angle,360.0))); 1382 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0)))); 1383 affine.sy=cos(DegreesToRadians(fmod(angle,360.0))); 1384 break; 1385 } 1386 break; 1387 } 1388 case 'S': 1389 case 's': 1390 { 1391 if (LocaleCompare(keyword,"scale") == 0) 1392 { 1393 for (p=(const char *) value; *p != '\0'; p++) 1394 if ((isspace((int) ((unsigned char) *p)) != 0) || 1395 (*p == ',')) 1396 break; 1397 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value); 1398 affine.sy=affine.sx; 1399 if (*p != '\0') 1400 affine.sy= 1401 GetUserSpaceCoordinateValue(svg_info,-1,p+1); 1402 svg_info->scale[svg_info->n]=ExpandAffine(&affine); 1403 break; 1404 } 1405 if (LocaleCompare(keyword,"skewX") == 0) 1406 { 1407 affine.sx=svg_info->affine.sx; 1408 affine.ry=tan(DegreesToRadians(fmod( 1409 GetUserSpaceCoordinateValue(svg_info,1,value), 1410 360.0))); 1411 affine.sy=svg_info->affine.sy; 1412 break; 1413 } 1414 if (LocaleCompare(keyword,"skewY") == 0) 1415 { 1416 affine.sx=svg_info->affine.sx; 1417 affine.rx=tan(DegreesToRadians(fmod( 1418 GetUserSpaceCoordinateValue(svg_info,-1,value), 1419 360.0))); 1420 affine.sy=svg_info->affine.sy; 1421 break; 1422 } 1423 break; 1424 } 1425 case 'T': 1426 case 't': 1427 { 1428 if (LocaleCompare(keyword,"translate") == 0) 1429 { 1430 for (p=(const char *) value; *p != '\0'; p++) 1431 if ((isspace((int) ((unsigned char) *p)) != 0) || 1432 (*p == ',')) 1433 break; 1434 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value); 1435 affine.ty=affine.tx; 1436 if (*p != '\0') 1437 affine.ty= 1438 GetUserSpaceCoordinateValue(svg_info,-1,p+1); 1439 break; 1440 } 1441 break; 1442 } 1443 default: 1444 break; 1445 } 1446 transform.sx=affine.sx*current.sx+affine.ry*current.rx; 1447 transform.rx=affine.rx*current.sx+affine.sy*current.rx; 1448 transform.ry=affine.sx*current.ry+affine.ry*current.sy; 1449 transform.sy=affine.rx*current.ry+affine.sy*current.sy; 1450 transform.tx=affine.tx*current.sx+affine.ty*current.ry+ 1451 current.tx; 1452 transform.ty=affine.tx*current.rx+affine.ty*current.sy+ 1453 current.ty; 1454 } 1455 (void) FormatLocaleFile(svg_info->file, 1456 "affine %g %g %g %g %g %g\n",transform.sx, 1457 transform.rx,transform.ry,transform.sy,transform.tx, 1458 transform.ty); 1459 for (j=0; tokens[j] != (char *) NULL; j++) 1460 tokens[j]=DestroyString(tokens[j]); 1461 tokens=(char **) RelinquishMagickMemory(tokens); 1462 break; 1463 } 1464 if (LocaleCompare(keyword,"gradientUnits") == 0) 1465 { 1466 (void) CloneString(&units,value); 1467 (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n", 1468 value); 1469 break; 1470 } 1471 break; 1472 } 1473 case 'H': 1474 case 'h': 1475 { 1476 if (LocaleCompare(keyword,"height") == 0) 1477 { 1478 svg_info->bounds.height= 1479 GetUserSpaceCoordinateValue(svg_info,-1,value); 1480 break; 1481 } 1482 if (LocaleCompare(keyword,"href") == 0) 1483 { 1484 (void) CloneString(&svg_info->url,value); 1485 break; 1486 } 1487 break; 1488 } 1489 case 'M': 1490 case 'm': 1491 { 1492 if (LocaleCompare(keyword,"major") == 0) 1493 { 1494 svg_info->element.major= 1495 GetUserSpaceCoordinateValue(svg_info,1,value); 1496 break; 1497 } 1498 if (LocaleCompare(keyword,"minor") == 0) 1499 { 1500 svg_info->element.minor= 1501 GetUserSpaceCoordinateValue(svg_info,-1,value); 1502 break; 1503 } 1504 break; 1505 } 1506 case 'O': 1507 case 'o': 1508 { 1509 if (LocaleCompare(keyword,"offset") == 0) 1510 { 1511 (void) CloneString(&svg_info->offset,value); 1512 break; 1513 } 1514 if (LocaleCompare(keyword,"opacity") == 0) 1515 { 1516 (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value); 1517 break; 1518 } 1519 break; 1520 } 1521 case 'P': 1522 case 'p': 1523 { 1524 if (LocaleCompare(keyword,"path") == 0) 1525 { 1526 (void) CloneString(&svg_info->url,value); 1527 break; 1528 } 1529 if (LocaleCompare(keyword,"points") == 0) 1530 { 1531 (void) CloneString(&svg_info->vertices,value); 1532 break; 1533 } 1534 break; 1535 } 1536 case 'R': 1537 case 'r': 1538 { 1539 if (LocaleCompare(keyword,"r") == 0) 1540 { 1541 svg_info->element.major= 1542 GetUserSpaceCoordinateValue(svg_info,1,value); 1543 svg_info->element.minor= 1544 GetUserSpaceCoordinateValue(svg_info,-1,value); 1545 break; 1546 } 1547 if (LocaleCompare(keyword,"rotate") == 0) 1548 { 1549 double 1550 angle; 1551 1552 angle=GetUserSpaceCoordinateValue(svg_info,0,value); 1553 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n", 1554 svg_info->bounds.x,svg_info->bounds.y); 1555 svg_info->bounds.x=0; 1556 svg_info->bounds.y=0; 1557 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle); 1558 break; 1559 } 1560 if (LocaleCompare(keyword,"rx") == 0) 1561 { 1562 if (LocaleCompare((const char *) name,"ellipse") == 0) 1563 svg_info->element.major= 1564 GetUserSpaceCoordinateValue(svg_info,1,value); 1565 else 1566 svg_info->radius.x= 1567 GetUserSpaceCoordinateValue(svg_info,1,value); 1568 break; 1569 } 1570 if (LocaleCompare(keyword,"ry") == 0) 1571 { 1572 if (LocaleCompare((const char *) name,"ellipse") == 0) 1573 svg_info->element.minor= 1574 GetUserSpaceCoordinateValue(svg_info,-1,value); 1575 else 1576 svg_info->radius.y= 1577 GetUserSpaceCoordinateValue(svg_info,-1,value); 1578 break; 1579 } 1580 break; 1581 } 1582 case 'S': 1583 case 's': 1584 { 1585 if (LocaleCompare(keyword,"stop-color") == 0) 1586 { 1587 (void) CloneString(&svg_info->stop_color,value); 1588 break; 1589 } 1590 if (LocaleCompare(keyword,"stroke") == 0) 1591 { 1592 if (LocaleCompare(value,"currentColor") == 0) 1593 { 1594 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color); 1595 break; 1596 } 1597 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value); 1598 break; 1599 } 1600 if (LocaleCompare(keyword,"stroke-antialiasing") == 0) 1601 { 1602 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n", 1603 LocaleCompare(value,"true") == 0); 1604 break; 1605 } 1606 if (LocaleCompare(keyword,"stroke-dasharray") == 0) 1607 { 1608 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n", 1609 value); 1610 break; 1611 } 1612 if (LocaleCompare(keyword,"stroke-dashoffset") == 0) 1613 { 1614 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n", 1615 value); 1616 break; 1617 } 1618 if (LocaleCompare(keyword,"stroke-linecap") == 0) 1619 { 1620 (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n", 1621 value); 1622 break; 1623 } 1624 if (LocaleCompare(keyword,"stroke-linejoin") == 0) 1625 { 1626 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n", 1627 value); 1628 break; 1629 } 1630 if (LocaleCompare(keyword,"stroke-miterlimit") == 0) 1631 { 1632 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n", 1633 value); 1634 break; 1635 } 1636 if (LocaleCompare(keyword,"stroke-opacity") == 0) 1637 { 1638 (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n", 1639 value); 1640 break; 1641 } 1642 if (LocaleCompare(keyword,"stroke-width") == 0) 1643 { 1644 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n", 1645 GetUserSpaceCoordinateValue(svg_info,1,value)); 1646 break; 1647 } 1648 if (LocaleCompare(keyword,"style") == 0) 1649 { 1650 (void) LogMagickEvent(CoderEvent,GetMagickModule()," "); 1651 tokens=GetStyleTokens(context,value,&number_tokens); 1652 for (j=0; j < (number_tokens-1); j+=2) 1653 { 1654 keyword=(char *) tokens[j]; 1655 value=(char *) tokens[j+1]; 1656 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1657 " %s: %s",keyword,value); 1658 switch (*keyword) 1659 { 1660 case 'C': 1661 case 'c': 1662 { 1663 if (LocaleCompare(keyword,"clip-path") == 0) 1664 { 1665 (void) FormatLocaleFile(svg_info->file, 1666 "clip-path '%s'\n",value); 1667 break; 1668 } 1669 if (LocaleCompare(keyword,"clip-rule") == 0) 1670 { 1671 (void) FormatLocaleFile(svg_info->file, 1672 "clip-rule '%s'\n",value); 1673 break; 1674 } 1675 if (LocaleCompare(keyword,"clipPathUnits") == 0) 1676 { 1677 (void) CloneString(&units,value); 1678 (void) FormatLocaleFile(svg_info->file, 1679 "clip-units '%s'\n",value); 1680 break; 1681 } 1682 if (LocaleCompare(keyword,"color") == 0) 1683 { 1684 (void) CloneString(&color,value); 1685 break; 1686 } 1687 break; 1688 } 1689 case 'F': 1690 case 'f': 1691 { 1692 if (LocaleCompare(keyword,"fill") == 0) 1693 { 1694 if (LocaleCompare(value,"currentColor") == 0) 1695 { 1696 (void) FormatLocaleFile(svg_info->file, 1697 "fill '%s'\n",color); 1698 break; 1699 } 1700 if (LocaleCompare(value,"#000000ff") == 0) 1701 (void) FormatLocaleFile(svg_info->file, 1702 "fill '#000000'\n"); 1703 else 1704 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n", 1705 value); 1706 break; 1707 } 1708 if (LocaleCompare(keyword,"fillcolor") == 0) 1709 { 1710 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n", 1711 value); 1712 break; 1713 } 1714 if (LocaleCompare(keyword,"fill-rule") == 0) 1715 { 1716 (void) FormatLocaleFile(svg_info->file, 1717 "fill-rule '%s'\n",value); 1718 break; 1719 } 1720 if (LocaleCompare(keyword,"fill-alpha") == 0) 1721 { 1722 (void) FormatLocaleFile(svg_info->file, 1723 "fill-alpha '%s'\n",value); 1724 break; 1725 } 1726 if (LocaleCompare(keyword,"font-family") == 0) 1727 { 1728 (void) FormatLocaleFile(svg_info->file, 1729 "font-family '%s'\n",value); 1730 break; 1731 } 1732 if (LocaleCompare(keyword,"font-stretch") == 0) 1733 { 1734 (void) FormatLocaleFile(svg_info->file, 1735 "font-stretch '%s'\n",value); 1736 break; 1737 } 1738 if (LocaleCompare(keyword,"font-style") == 0) 1739 { 1740 (void) FormatLocaleFile(svg_info->file, 1741 "font-style '%s'\n",value); 1742 break; 1743 } 1744 if (LocaleCompare(keyword,"font-size") == 0) 1745 { 1746 svg_info->pointsize=GetUserSpaceCoordinateValue( 1747 svg_info,0,value); 1748 (void) FormatLocaleFile(svg_info->file,"font-size %g\n", 1749 svg_info->pointsize); 1750 break; 1751 } 1752 if (LocaleCompare(keyword,"font-weight") == 0) 1753 { 1754 (void) FormatLocaleFile(svg_info->file, 1755 "font-weight '%s'\n",value); 1756 break; 1757 } 1758 break; 1759 } 1760 case 'O': 1761 case 'o': 1762 { 1763 if (LocaleCompare(keyword,"offset") == 0) 1764 { 1765 (void) FormatLocaleFile(svg_info->file,"offset %g\n", 1766 GetUserSpaceCoordinateValue(svg_info,1,value)); 1767 break; 1768 } 1769 if (LocaleCompare(keyword,"opacity") == 0) 1770 { 1771 (void) FormatLocaleFile(svg_info->file, 1772 "opacity '%s'\n",value); 1773 break; 1774 } 1775 break; 1776 } 1777 case 'S': 1778 case 's': 1779 { 1780 if (LocaleCompare(keyword,"stop-color") == 0) 1781 { 1782 (void) CloneString(&svg_info->stop_color,value); 1783 break; 1784 } 1785 if (LocaleCompare(keyword,"stroke") == 0) 1786 { 1787 if (LocaleCompare(value,"currentColor") == 0) 1788 { 1789 (void) FormatLocaleFile(svg_info->file, 1790 "stroke '%s'\n",color); 1791 break; 1792 } 1793 if (LocaleCompare(value,"#000000ff") == 0) 1794 (void) FormatLocaleFile(svg_info->file, 1795 "fill '#000000'\n"); 1796 else 1797 (void) FormatLocaleFile(svg_info->file, 1798 "stroke '%s'\n",value); 1799 break; 1800 } 1801 if (LocaleCompare(keyword,"stroke-antialiasing") == 0) 1802 { 1803 (void) FormatLocaleFile(svg_info->file, 1804 "stroke-antialias %d\n", 1805 LocaleCompare(value,"true") == 0); 1806 break; 1807 } 1808 if (LocaleCompare(keyword,"stroke-dasharray") == 0) 1809 { 1810 (void) FormatLocaleFile(svg_info->file, 1811 "stroke-dasharray %s\n",value); 1812 break; 1813 } 1814 if (LocaleCompare(keyword,"stroke-dashoffset") == 0) 1815 { 1816 (void) FormatLocaleFile(svg_info->file, 1817 "stroke-dashoffset %s\n",value); 1818 break; 1819 } 1820 if (LocaleCompare(keyword,"stroke-linecap") == 0) 1821 { 1822 (void) FormatLocaleFile(svg_info->file, 1823 "stroke-linecap '%s'\n",value); 1824 break; 1825 } 1826 if (LocaleCompare(keyword,"stroke-linejoin") == 0) 1827 { 1828 (void) FormatLocaleFile(svg_info->file, 1829 "stroke-linejoin '%s'\n",value); 1830 break; 1831 } 1832 if (LocaleCompare(keyword,"stroke-miterlimit") == 0) 1833 { 1834 (void) FormatLocaleFile(svg_info->file, 1835 "stroke-miterlimit '%s'\n",value); 1836 break; 1837 } 1838 if (LocaleCompare(keyword,"stroke-opacity") == 0) 1839 { 1840 (void) FormatLocaleFile(svg_info->file, 1841 "stroke-opacity '%s'\n",value); 1842 break; 1843 } 1844 if (LocaleCompare(keyword,"stroke-width") == 0) 1845 { 1846 (void) FormatLocaleFile(svg_info->file, 1847 "stroke-width %g\n", 1848 GetUserSpaceCoordinateValue(svg_info,1,value)); 1849 break; 1850 } 1851 break; 1852 } 1853 case 't': 1854 case 'T': 1855 { 1856 if (LocaleCompare(keyword,"text-align") == 0) 1857 { 1858 (void) FormatLocaleFile(svg_info->file, 1859 "text-align '%s'\n",value); 1860 break; 1861 } 1862 if (LocaleCompare(keyword,"text-anchor") == 0) 1863 { 1864 (void) FormatLocaleFile(svg_info->file, 1865 "text-anchor '%s'\n",value); 1866 break; 1867 } 1868 if (LocaleCompare(keyword,"text-decoration") == 0) 1869 { 1870 if (LocaleCompare(value,"underline") == 0) 1871 (void) FormatLocaleFile(svg_info->file, 1872 "decorate underline\n"); 1873 if (LocaleCompare(value,"line-through") == 0) 1874 (void) FormatLocaleFile(svg_info->file, 1875 "decorate line-through\n"); 1876 if (LocaleCompare(value,"overline") == 0) 1877 (void) FormatLocaleFile(svg_info->file, 1878 "decorate overline\n"); 1879 break; 1880 } 1881 if (LocaleCompare(keyword,"text-antialiasing") == 0) 1882 { 1883 (void) FormatLocaleFile(svg_info->file, 1884 "text-antialias %d\n", 1885 LocaleCompare(value,"true") == 0); 1886 break; 1887 } 1888 break; 1889 } 1890 default: 1891 break; 1892 } 1893 } 1894 for (j=0; tokens[j] != (char *) NULL; j++) 1895 tokens[j]=DestroyString(tokens[j]); 1896 tokens=(char **) RelinquishMagickMemory(tokens); 1897 break; 1898 } 1899 break; 1900 } 1901 case 'T': 1902 case 't': 1903 { 1904 if (LocaleCompare(keyword,"text-align") == 0) 1905 { 1906 (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n", 1907 value); 1908 break; 1909 } 1910 if (LocaleCompare(keyword,"text-anchor") == 0) 1911 { 1912 (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n", 1913 value); 1914 break; 1915 } 1916 if (LocaleCompare(keyword,"text-decoration") == 0) 1917 { 1918 if (LocaleCompare(value,"underline") == 0) 1919 (void) FormatLocaleFile(svg_info->file,"decorate underline\n"); 1920 if (LocaleCompare(value,"line-through") == 0) 1921 (void) FormatLocaleFile(svg_info->file, 1922 "decorate line-through\n"); 1923 if (LocaleCompare(value,"overline") == 0) 1924 (void) FormatLocaleFile(svg_info->file,"decorate overline\n"); 1925 break; 1926 } 1927 if (LocaleCompare(keyword,"text-antialiasing") == 0) 1928 { 1929 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n", 1930 LocaleCompare(value,"true") == 0); 1931 break; 1932 } 1933 if (LocaleCompare(keyword,"transform") == 0) 1934 { 1935 AffineMatrix 1936 affine, 1937 current, 1938 transform; 1939 1940 GetAffineMatrix(&transform); 1941 (void) LogMagickEvent(CoderEvent,GetMagickModule()," "); 1942 tokens=GetTransformTokens(context,value,&number_tokens); 1943 if (tokens == (char **) NULL) 1944 break; 1945 for (j=0; j < (number_tokens-1); j+=2) 1946 { 1947 keyword=(char *) tokens[j]; 1948 value=(char *) tokens[j+1]; 1949 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1950 " %s: %s",keyword,value); 1951 current=transform; 1952 GetAffineMatrix(&affine); 1953 switch (*keyword) 1954 { 1955 case 'M': 1956 case 'm': 1957 { 1958 if (LocaleCompare(keyword,"matrix") == 0) 1959 { 1960 p=(const char *) value; 1961 GetNextToken(p,&p,MagickPathExtent,token); 1962 affine.sx=StringToDouble(value,(char **) NULL); 1963 GetNextToken(p,&p,MagickPathExtent,token); 1964 if (*token == ',') 1965 GetNextToken(p,&p,MagickPathExtent,token); 1966 affine.rx=StringToDouble(token,&next_token); 1967 GetNextToken(p,&p,MagickPathExtent,token); 1968 if (*token == ',') 1969 GetNextToken(p,&p,MagickPathExtent,token); 1970 affine.ry=StringToDouble(token,&next_token); 1971 GetNextToken(p,&p,MagickPathExtent,token); 1972 if (*token == ',') 1973 GetNextToken(p,&p,MagickPathExtent,token); 1974 affine.sy=StringToDouble(token,&next_token); 1975 GetNextToken(p,&p,MagickPathExtent,token); 1976 if (*token == ',') 1977 GetNextToken(p,&p,MagickPathExtent,token); 1978 affine.tx=StringToDouble(token,&next_token); 1979 GetNextToken(p,&p,MagickPathExtent,token); 1980 if (*token == ',') 1981 GetNextToken(p,&p,MagickPathExtent,token); 1982 affine.ty=StringToDouble(token,&next_token); 1983 break; 1984 } 1985 break; 1986 } 1987 case 'R': 1988 case 'r': 1989 { 1990 if (LocaleCompare(keyword,"rotate") == 0) 1991 { 1992 double 1993 angle, 1994 x, 1995 y; 1996 1997 p=(const char *) value; 1998 GetNextToken(p,&p,MagickPathExtent,token); 1999 angle=StringToDouble(value,(char **) NULL); 2000 GetNextToken(p,&p,MagickPathExtent,token); 2001 if (*token == ',') 2002 GetNextToken(p,&p,MagickPathExtent,token); 2003 x=StringToDouble(token,&next_token); 2004 GetNextToken(p,&p,MagickPathExtent,token); 2005 if (*token == ',') 2006 GetNextToken(p,&p,MagickPathExtent,token); 2007 y=StringToDouble(token,&next_token); 2008 affine.sx=cos(DegreesToRadians(fmod(angle,360.0))); 2009 affine.rx=sin(DegreesToRadians(fmod(angle,360.0))); 2010 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0)))); 2011 affine.sy=cos(DegreesToRadians(fmod(angle,360.0))); 2012 affine.tx=x; 2013 affine.ty=y; 2014 svg_info->center.x=x; 2015 svg_info->center.y=y; 2016 break; 2017 } 2018 break; 2019 } 2020 case 'S': 2021 case 's': 2022 { 2023 if (LocaleCompare(keyword,"scale") == 0) 2024 { 2025 for (p=(const char *) value; *p != '\0'; p++) 2026 if ((isspace((int) ((unsigned char) *p)) != 0) || 2027 (*p == ',')) 2028 break; 2029 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value); 2030 affine.sy=affine.sx; 2031 if (*p != '\0') 2032 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1, 2033 p+1); 2034 svg_info->scale[svg_info->n]=ExpandAffine(&affine); 2035 break; 2036 } 2037 if (LocaleCompare(keyword,"skewX") == 0) 2038 { 2039 affine.sx=svg_info->affine.sx; 2040 affine.ry=tan(DegreesToRadians(fmod( 2041 GetUserSpaceCoordinateValue(svg_info,1,value), 2042 360.0))); 2043 affine.sy=svg_info->affine.sy; 2044 break; 2045 } 2046 if (LocaleCompare(keyword,"skewY") == 0) 2047 { 2048 affine.sx=svg_info->affine.sx; 2049 affine.rx=tan(DegreesToRadians(fmod( 2050 GetUserSpaceCoordinateValue(svg_info,-1,value), 2051 360.0))); 2052 affine.sy=svg_info->affine.sy; 2053 break; 2054 } 2055 break; 2056 } 2057 case 'T': 2058 case 't': 2059 { 2060 if (LocaleCompare(keyword,"translate") == 0) 2061 { 2062 for (p=(const char *) value; *p != '\0'; p++) 2063 if ((isspace((int) ((unsigned char) *p)) != 0) || 2064 (*p == ',')) 2065 break; 2066 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value); 2067 affine.ty=affine.tx; 2068 if (*p != '\0') 2069 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1, 2070 p+1); 2071 break; 2072 } 2073 break; 2074 } 2075 default: 2076 break; 2077 } 2078 transform.sx=affine.sx*current.sx+affine.ry*current.rx; 2079 transform.rx=affine.rx*current.sx+affine.sy*current.rx; 2080 transform.ry=affine.sx*current.ry+affine.ry*current.sy; 2081 transform.sy=affine.rx*current.ry+affine.sy*current.sy; 2082 transform.tx=affine.tx*current.sx+affine.ty*current.ry+ 2083 current.tx; 2084 transform.ty=affine.tx*current.rx+affine.ty*current.sy+ 2085 current.ty; 2086 } 2087 (void) FormatLocaleFile(svg_info->file, 2088 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx, 2089 transform.ry,transform.sy,transform.tx,transform.ty); 2090 for (j=0; tokens[j] != (char *) NULL; j++) 2091 tokens[j]=DestroyString(tokens[j]); 2092 tokens=(char **) RelinquishMagickMemory(tokens); 2093 break; 2094 } 2095 break; 2096 } 2097 case 'V': 2098 case 'v': 2099 { 2100 if (LocaleCompare(keyword,"verts") == 0) 2101 { 2102 (void) CloneString(&svg_info->vertices,value); 2103 break; 2104 } 2105 if (LocaleCompare(keyword,"viewBox") == 0) 2106 { 2107 p=(const char *) value; 2108 GetNextToken(p,&p,MagickPathExtent,token); 2109 svg_info->view_box.x=StringToDouble(token,&next_token); 2110 GetNextToken(p,&p,MagickPathExtent,token); 2111 if (*token == ',') 2112 GetNextToken(p,&p,MagickPathExtent,token); 2113 svg_info->view_box.y=StringToDouble(token,&next_token); 2114 GetNextToken(p,&p,MagickPathExtent,token); 2115 if (*token == ',') 2116 GetNextToken(p,&p,MagickPathExtent,token); 2117 svg_info->view_box.width=StringToDouble(token, 2118 (char **) NULL); 2119 if (svg_info->bounds.width == 0) 2120 svg_info->bounds.width=svg_info->view_box.width; 2121 GetNextToken(p,&p,MagickPathExtent,token); 2122 if (*token == ',') 2123 GetNextToken(p,&p,MagickPathExtent,token); 2124 svg_info->view_box.height=StringToDouble(token, 2125 (char **) NULL); 2126 if (svg_info->bounds.height == 0) 2127 svg_info->bounds.height=svg_info->view_box.height; 2128 break; 2129 } 2130 break; 2131 } 2132 case 'W': 2133 case 'w': 2134 { 2135 if (LocaleCompare(keyword,"width") == 0) 2136 { 2137 svg_info->bounds.width= 2138 GetUserSpaceCoordinateValue(svg_info,1,value); 2139 break; 2140 } 2141 break; 2142 } 2143 case 'X': 2144 case 'x': 2145 { 2146 if (LocaleCompare(keyword,"x") == 0) 2147 { 2148 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value); 2149 break; 2150 } 2151 if (LocaleCompare(keyword,"xlink:href") == 0) 2152 { 2153 (void) CloneString(&svg_info->url,value); 2154 break; 2155 } 2156 if (LocaleCompare(keyword,"x1") == 0) 2157 { 2158 svg_info->segment.x1= 2159 GetUserSpaceCoordinateValue(svg_info,1,value); 2160 break; 2161 } 2162 if (LocaleCompare(keyword,"x2") == 0) 2163 { 2164 svg_info->segment.x2= 2165 GetUserSpaceCoordinateValue(svg_info,1,value); 2166 break; 2167 } 2168 break; 2169 } 2170 case 'Y': 2171 case 'y': 2172 { 2173 if (LocaleCompare(keyword,"y") == 0) 2174 { 2175 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value); 2176 break; 2177 } 2178 if (LocaleCompare(keyword,"y1") == 0) 2179 { 2180 svg_info->segment.y1= 2181 GetUserSpaceCoordinateValue(svg_info,-1,value); 2182 break; 2183 } 2184 if (LocaleCompare(keyword,"y2") == 0) 2185 { 2186 svg_info->segment.y2= 2187 GetUserSpaceCoordinateValue(svg_info,-1,value); 2188 break; 2189 } 2190 break; 2191 } 2192 default: 2193 break; 2194 } 2195 } 2196 if (LocaleCompare((const char *) name,"svg") == 0) 2197 { 2198 if (svg_info->document->encoding != (const xmlChar *) NULL) 2199 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n", 2200 (const char *) svg_info->document->encoding); 2201 if (attributes != (const xmlChar **) NULL) 2202 { 2203 double 2204 sx, 2205 sy, 2206 tx, 2207 ty; 2208 2209 if ((svg_info->view_box.width == 0.0) || 2210 (svg_info->view_box.height == 0.0)) 2211 svg_info->view_box=svg_info->bounds; 2212 svg_info->width=0; 2213 if (svg_info->bounds.width > 0.0) 2214 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5); 2215 svg_info->height=0; 2216 if (svg_info->bounds.height > 0.0) 2217 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5); 2218 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n", 2219 (double) svg_info->width,(double) svg_info->height); 2220 sx=(double) svg_info->width/svg_info->view_box.width; 2221 sy=(double) svg_info->height/svg_info->view_box.height; 2222 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x : 2223 0.0; 2224 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y : 2225 0.0; 2226 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n", 2227 sx,sy,tx,ty); 2228 } 2229 } 2230 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )"); 2231 units=DestroyString(units); 2232 if (color != (char *) NULL) 2233 color=DestroyString(color); 2234 } 2235 2236 static void SVGEndElement(void *context,const xmlChar *name) 2237 { 2238 SVGInfo 2239 *svg_info; 2240 2241 /* 2242 Called when the end of an element has been detected. 2243 */ 2244 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 2245 " SAX.endElement(%s)",name); 2246 svg_info=(SVGInfo *) context; 2247 if (strchr((char *) name,':') != (char *) NULL) 2248 { 2249 /* 2250 Skip over namespace. 2251 */ 2252 for ( ; *name != ':'; name++) ; 2253 name++; 2254 } 2255 switch (*name) 2256 { 2257 case 'C': 2258 case 'c': 2259 { 2260 if (LocaleCompare((const char *) name,"circle") == 0) 2261 { 2262 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n", 2263 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx, 2264 svg_info->element.cy+svg_info->element.minor); 2265 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2266 break; 2267 } 2268 if (LocaleCompare((const char *) name,"clipPath") == 0) 2269 { 2270 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n"); 2271 break; 2272 } 2273 break; 2274 } 2275 case 'D': 2276 case 'd': 2277 { 2278 if (LocaleCompare((const char *) name,"defs") == 0) 2279 { 2280 (void) FormatLocaleFile(svg_info->file,"pop defs\n"); 2281 break; 2282 } 2283 if (LocaleCompare((const char *) name,"desc") == 0) 2284 { 2285 register char 2286 *p; 2287 2288 if (*svg_info->text == '\0') 2289 break; 2290 (void) fputc('#',svg_info->file); 2291 for (p=svg_info->text; *p != '\0'; p++) 2292 { 2293 (void) fputc(*p,svg_info->file); 2294 if (*p == '\n') 2295 (void) fputc('#',svg_info->file); 2296 } 2297 (void) fputc('\n',svg_info->file); 2298 *svg_info->text='\0'; 2299 break; 2300 } 2301 break; 2302 } 2303 case 'E': 2304 case 'e': 2305 { 2306 if (LocaleCompare((const char *) name,"ellipse") == 0) 2307 { 2308 double 2309 angle; 2310 2311 angle=svg_info->element.angle; 2312 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n", 2313 svg_info->element.cx,svg_info->element.cy, 2314 angle == 0.0 ? svg_info->element.major : svg_info->element.minor, 2315 angle == 0.0 ? svg_info->element.minor : svg_info->element.major); 2316 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2317 break; 2318 } 2319 break; 2320 } 2321 case 'G': 2322 case 'g': 2323 { 2324 if (LocaleCompare((const char *) name,"g") == 0) 2325 { 2326 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2327 break; 2328 } 2329 break; 2330 } 2331 case 'I': 2332 case 'i': 2333 { 2334 if (LocaleCompare((const char *) name,"image") == 0) 2335 { 2336 (void) FormatLocaleFile(svg_info->file, 2337 "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x, 2338 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height, 2339 svg_info->url); 2340 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2341 break; 2342 } 2343 break; 2344 } 2345 case 'L': 2346 case 'l': 2347 { 2348 if (LocaleCompare((const char *) name,"line") == 0) 2349 { 2350 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n", 2351 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2, 2352 svg_info->segment.y2); 2353 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2354 break; 2355 } 2356 if (LocaleCompare((const char *) name,"linearGradient") == 0) 2357 { 2358 (void) FormatLocaleFile(svg_info->file,"pop gradient\n"); 2359 break; 2360 } 2361 break; 2362 } 2363 case 'P': 2364 case 'p': 2365 { 2366 if (LocaleCompare((const char *) name,"pattern") == 0) 2367 { 2368 (void) FormatLocaleFile(svg_info->file,"pop pattern\n"); 2369 break; 2370 } 2371 if (LocaleCompare((const char *) name,"path") == 0) 2372 { 2373 (void) FormatLocaleFile(svg_info->file,"path '%s'\n", 2374 svg_info->vertices); 2375 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2376 break; 2377 } 2378 if (LocaleCompare((const char *) name,"polygon") == 0) 2379 { 2380 (void) FormatLocaleFile(svg_info->file,"polygon %s\n", 2381 svg_info->vertices); 2382 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2383 break; 2384 } 2385 if (LocaleCompare((const char *) name,"polyline") == 0) 2386 { 2387 (void) FormatLocaleFile(svg_info->file,"polyline %s\n", 2388 svg_info->vertices); 2389 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2390 break; 2391 } 2392 break; 2393 } 2394 case 'R': 2395 case 'r': 2396 { 2397 if (LocaleCompare((const char *) name,"radialGradient") == 0) 2398 { 2399 (void) FormatLocaleFile(svg_info->file,"pop gradient\n"); 2400 break; 2401 } 2402 if (LocaleCompare((const char *) name,"rect") == 0) 2403 { 2404 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0)) 2405 { 2406 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n", 2407 svg_info->bounds.x,svg_info->bounds.y, 2408 svg_info->bounds.x+svg_info->bounds.width, 2409 svg_info->bounds.y+svg_info->bounds.height); 2410 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2411 break; 2412 } 2413 if (svg_info->radius.x == 0.0) 2414 svg_info->radius.x=svg_info->radius.y; 2415 if (svg_info->radius.y == 0.0) 2416 svg_info->radius.y=svg_info->radius.x; 2417 (void) FormatLocaleFile(svg_info->file, 2418 "roundRectangle %g,%g %g,%g %g,%g\n", 2419 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+ 2420 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height, 2421 svg_info->radius.x,svg_info->radius.y); 2422 svg_info->radius.x=0.0; 2423 svg_info->radius.y=0.0; 2424 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2425 break; 2426 } 2427 break; 2428 } 2429 case 'S': 2430 case 's': 2431 { 2432 if (LocaleCompare((const char *) name,"stop") == 0) 2433 { 2434 (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n", 2435 svg_info->stop_color,svg_info->offset); 2436 break; 2437 } 2438 if (LocaleCompare((const char *) name,"svg") == 0) 2439 { 2440 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2441 break; 2442 } 2443 break; 2444 } 2445 case 'T': 2446 case 't': 2447 { 2448 if (LocaleCompare((const char *) name,"text") == 0) 2449 { 2450 if (*svg_info->text != '\0') 2451 { 2452 char 2453 *text; 2454 2455 text=EscapeString(svg_info->text,'\''); 2456 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n", 2457 svg_info->bounds.x,svg_info->bounds.y,text); 2458 text=DestroyString(text); 2459 *svg_info->text='\0'; 2460 } 2461 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2462 break; 2463 } 2464 if (LocaleCompare((const char *) name,"tspan") == 0) 2465 { 2466 if (*svg_info->text != '\0') 2467 { 2468 DrawInfo 2469 *draw_info; 2470 2471 TypeMetric 2472 metrics; 2473 2474 char 2475 *text; 2476 2477 text=EscapeString(svg_info->text,'\''); 2478 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n", 2479 svg_info->bounds.x,svg_info->bounds.y,text); 2480 text=DestroyString(text); 2481 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL); 2482 draw_info->pointsize=svg_info->pointsize; 2483 draw_info->text=AcquireString(svg_info->text); 2484 (void) ConcatenateString(&draw_info->text," "); 2485 (void) GetTypeMetrics(svg_info->image,draw_info,&metrics, 2486 svg_info->exception); 2487 svg_info->bounds.x+=metrics.width; 2488 draw_info=DestroyDrawInfo(draw_info); 2489 *svg_info->text='\0'; 2490 } 2491 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); 2492 break; 2493 } 2494 if (LocaleCompare((const char *) name,"title") == 0) 2495 { 2496 if (*svg_info->text == '\0') 2497 break; 2498 (void) CloneString(&svg_info->title,svg_info->text); 2499 *svg_info->text='\0'; 2500 break; 2501 } 2502 break; 2503 } 2504 default: 2505 break; 2506 } 2507 *svg_info->text='\0'; 2508 (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element)); 2509 (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment)); 2510 svg_info->n--; 2511 } 2512 2513 static void SVGCharacters(void *context,const xmlChar *c,int length) 2514 { 2515 char 2516 *text; 2517 2518 register char 2519 *p; 2520 2521 register ssize_t 2522 i; 2523 2524 SVGInfo 2525 *svg_info; 2526 2527 /* 2528 Receiving some characters from the parser. 2529 */ 2530 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 2531 " SAX.characters(%s,%.20g)",c,(double) length); 2532 svg_info=(SVGInfo *) context; 2533 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text)); 2534 if (text == (char *) NULL) 2535 return; 2536 p=text; 2537 for (i=0; i < (ssize_t) length; i++) 2538 *p++=c[i]; 2539 *p='\0'; 2540 StripString(text); 2541 if (svg_info->text == (char *) NULL) 2542 svg_info->text=text; 2543 else 2544 { 2545 (void) ConcatenateString(&svg_info->text,text); 2546 text=DestroyString(text); 2547 } 2548 } 2549 2550 static void SVGReference(void *context,const xmlChar *name) 2551 { 2552 SVGInfo 2553 *svg_info; 2554 2555 xmlParserCtxtPtr 2556 parser; 2557 2558 /* 2559 Called when an entity reference is detected. 2560 */ 2561 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)", 2562 name); 2563 svg_info=(SVGInfo *) context; 2564 parser=svg_info->parser; 2565 if (parser == (xmlParserCtxtPtr) NULL) 2566 return; 2567 if (parser->node == (xmlNodePtr) NULL) 2568 return; 2569 if (*name == '#') 2570 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name)); 2571 else 2572 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name)); 2573 } 2574 2575 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length) 2576 { 2577 SVGInfo 2578 *svg_info; 2579 2580 /* 2581 Receiving some ignorable whitespaces from the parser. 2582 */ 2583 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 2584 " SAX.ignorableWhitespace(%.30s, %d)",c,length); 2585 svg_info=(SVGInfo *) context; 2586 (void) svg_info; 2587 } 2588 2589 static void SVGProcessingInstructions(void *context,const xmlChar *target, 2590 const xmlChar *data) 2591 { 2592 SVGInfo 2593 *svg_info; 2594 2595 /* 2596 A processing instruction has been parsed. 2597 */ 2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 2599 " SAX.processingInstruction(%s, %s)",target,data); 2600 svg_info=(SVGInfo *) context; 2601 (void) svg_info; 2602 } 2603 2604 static void SVGComment(void *context,const xmlChar *value) 2605 { 2606 SVGInfo 2607 *svg_info; 2608 2609 /* 2610 A comment has been parsed. 2611 */ 2612 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)", 2613 value); 2614 svg_info=(SVGInfo *) context; 2615 if (svg_info->comment != (char *) NULL) 2616 (void) ConcatenateString(&svg_info->comment,"\n"); 2617 (void) ConcatenateString(&svg_info->comment,(const char *) value); 2618 } 2619 2620 static void SVGWarning(void *context,const char *format,...) 2621 { 2622 char 2623 *message, 2624 reason[MagickPathExtent]; 2625 2626 SVGInfo 2627 *svg_info; 2628 2629 va_list 2630 operands; 2631 2632 /** 2633 Display and format a warning messages, gives file, line, position and 2634 extra parameters. 2635 */ 2636 va_start(operands,format); 2637 svg_info=(SVGInfo *) context; 2638 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: "); 2639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands); 2640 #if !defined(MAGICKCORE_HAVE_VSNPRINTF) 2641 (void) vsprintf(reason,format,operands); 2642 #else 2643 (void) vsnprintf(reason,MagickPathExtent,format,operands); 2644 #endif 2645 message=GetExceptionMessage(errno); 2646 (void) ThrowMagickException(svg_info->exception,GetMagickModule(), 2647 DelegateWarning,reason,"`%s`",message); 2648 message=DestroyString(message); 2649 va_end(operands); 2650 } 2651 2652 static void SVGError(void *context,const char *format,...) 2653 { 2654 char 2655 *message, 2656 reason[MagickPathExtent]; 2657 2658 SVGInfo 2659 *svg_info; 2660 2661 va_list 2662 operands; 2663 2664 /* 2665 Display and format a error formats, gives file, line, position and 2666 extra parameters. 2667 */ 2668 va_start(operands,format); 2669 svg_info=(SVGInfo *) context; 2670 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: "); 2671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands); 2672 #if !defined(MAGICKCORE_HAVE_VSNPRINTF) 2673 (void) vsprintf(reason,format,operands); 2674 #else 2675 (void) vsnprintf(reason,MagickPathExtent,format,operands); 2676 #endif 2677 message=GetExceptionMessage(errno); 2678 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError, 2679 reason,"`%s`",message); 2680 message=DestroyString(message); 2681 va_end(operands); 2682 } 2683 2684 static void SVGCDataBlock(void *context,const xmlChar *value,int length) 2685 { 2686 SVGInfo 2687 *svg_info; 2688 2689 xmlNodePtr 2690 child; 2691 2692 xmlParserCtxtPtr 2693 parser; 2694 2695 /* 2696 Called when a pcdata block has been parsed. 2697 */ 2698 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)", 2699 value,length); 2700 svg_info=(SVGInfo *) context; 2701 parser=svg_info->parser; 2702 child=xmlGetLastChild(parser->node); 2703 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE)) 2704 { 2705 xmlTextConcat(child,value,length); 2706 return; 2707 } 2708 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length)); 2709 } 2710 2711 static void SVGExternalSubset(void *context,const xmlChar *name, 2712 const xmlChar *external_id,const xmlChar *system_id) 2713 { 2714 SVGInfo 2715 *svg_info; 2716 2717 xmlParserCtxt 2718 parser_context; 2719 2720 xmlParserCtxtPtr 2721 parser; 2722 2723 xmlParserInputPtr 2724 input; 2725 2726 /* 2727 Does this document has an external subset? 2728 */ 2729 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 2730 " SAX.externalSubset(%s, %s, %s)",name, 2731 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"), 2732 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none")); 2733 svg_info=(SVGInfo *) context; 2734 parser=svg_info->parser; 2735 if (((external_id == NULL) && (system_id == NULL)) || 2736 ((parser->validate == 0) || (parser->wellFormed == 0) || 2737 (svg_info->document == 0))) 2738 return; 2739 input=SVGResolveEntity(context,external_id,system_id); 2740 if (input == NULL) 2741 return; 2742 (void) xmlNewDtd(svg_info->document,name,external_id,system_id); 2743 parser_context=(*parser); 2744 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab)); 2745 if (parser->inputTab == (xmlParserInputPtr *) NULL) 2746 { 2747 parser->errNo=XML_ERR_NO_MEMORY; 2748 parser->input=parser_context.input; 2749 parser->inputNr=parser_context.inputNr; 2750 parser->inputMax=parser_context.inputMax; 2751 parser->inputTab=parser_context.inputTab; 2752 return; 2753 } 2754 parser->inputNr=0; 2755 parser->inputMax=5; 2756 parser->input=NULL; 2757 xmlPushInput(parser,input); 2758 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4)); 2759 if (input->filename == (char *) NULL) 2760 input->filename=(char *) xmlStrdup(system_id); 2761 input->line=1; 2762 input->col=1; 2763 input->base=parser->input->cur; 2764 input->cur=parser->input->cur; 2765 input->free=NULL; 2766 xmlParseExternalSubset(parser,external_id,system_id); 2767 while (parser->inputNr > 1) 2768 (void) xmlPopInput(parser); 2769 xmlFreeInputStream(parser->input); 2770 xmlFree(parser->inputTab); 2771 parser->input=parser_context.input; 2772 parser->inputNr=parser_context.inputNr; 2773 parser->inputMax=parser_context.inputMax; 2774 parser->inputTab=parser_context.inputTab; 2775 } 2776 2777 #if defined(__cplusplus) || defined(c_plusplus) 2778 } 2779 #endif 2780 2781 /* 2782 Static declarations. 2783 */ 2784 static char 2785 SVGDensityGeometry[] = "90.0x90.0"; 2786 2787 2788 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception) 2789 { 2790 char 2791 filename[MagickPathExtent]; 2792 2793 FILE 2794 *file; 2795 2796 Image 2797 *image; 2798 2799 int 2800 status, 2801 unique_file; 2802 2803 ssize_t 2804 n; 2805 2806 SVGInfo 2807 *svg_info; 2808 2809 unsigned char 2810 message[MagickPathExtent]; 2811 2812 xmlSAXHandler 2813 sax_modules; 2814 2815 xmlSAXHandlerPtr 2816 sax_handler; 2817 2818 /* 2819 Open image file. 2820 */ 2821 assert(image_info != (const ImageInfo *) NULL); 2822 assert(image_info->signature == MagickCoreSignature); 2823 assert(exception != (ExceptionInfo *) NULL); 2824 if (image_info->debug != MagickFalse) 2825 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 2826 image_info->filename); 2827 assert(exception->signature == MagickCoreSignature); 2828 image=AcquireImage(image_info,exception); 2829 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 2830 if (status == MagickFalse) 2831 { 2832 image=DestroyImageList(image); 2833 return((Image *) NULL); 2834 } 2835 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0)) 2836 { 2837 GeometryInfo 2838 geometry_info; 2839 2840 int 2841 flags; 2842 2843 flags=ParseGeometry(SVGDensityGeometry,&geometry_info); 2844 image->resolution.x=geometry_info.rho; 2845 image->resolution.y=geometry_info.sigma; 2846 if ((flags & SigmaValue) == 0) 2847 image->resolution.y=image->resolution.x; 2848 } 2849 if (LocaleCompare(image_info->magick,"MSVG") != 0) 2850 { 2851 const DelegateInfo 2852 *delegate_info; 2853 2854 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception); 2855 if (delegate_info != (const DelegateInfo *) NULL) 2856 { 2857 char 2858 background[MagickPathExtent], 2859 command[MagickPathExtent], 2860 *density, 2861 input_filename[MagickPathExtent], 2862 opacity[MagickPathExtent], 2863 output_filename[MagickPathExtent], 2864 unique[MagickPathExtent]; 2865 2866 int 2867 status; 2868 2869 struct stat 2870 attributes; 2871 2872 /* 2873 Our best hope of compliance with the SVG standard. 2874 */ 2875 status=AcquireUniqueSymbolicLink(image->filename,input_filename); 2876 (void) AcquireUniqueFilename(output_filename); 2877 (void) AcquireUniqueFilename(unique); 2878 density=AcquireString(""); 2879 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g", 2880 image->resolution.x,image->resolution.y); 2881 (void) FormatLocaleString(background,MagickPathExtent, 2882 "rgb(%.20g%%,%.20g%%,%.20g%%)", 2883 100.0*QuantumScale*image->background_color.red, 2884 100.0*QuantumScale*image->background_color.green, 2885 100.0*QuantumScale*image->background_color.blue); 2886 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g", 2887 QuantumScale*image->background_color.alpha); 2888 (void) FormatLocaleString(command,MagickPathExtent,GetDelegateCommands( 2889 delegate_info),input_filename,output_filename,density,background, 2890 opacity,unique); 2891 density=DestroyString(density); 2892 status=ExternalDelegateCommand(MagickFalse,image_info->verbose, 2893 command,(char *) NULL,exception); 2894 (void) RelinquishUniqueFileResource(unique); 2895 (void) RelinquishUniqueFileResource(input_filename); 2896 if ((status == 0) && (stat(output_filename,&attributes) == 0) && 2897 (attributes.st_size > 0)) 2898 { 2899 Image 2900 *svg_image; 2901 2902 ImageInfo 2903 *read_info; 2904 2905 read_info=CloneImageInfo(image_info); 2906 (void) CopyMagickString(read_info->filename,output_filename, 2907 MagickPathExtent); 2908 svg_image=ReadImage(read_info,exception); 2909 read_info=DestroyImageInfo(read_info); 2910 (void) RelinquishUniqueFileResource(output_filename); 2911 if (svg_image != (Image *) NULL) 2912 { 2913 image=DestroyImage(image); 2914 return(svg_image); 2915 } 2916 } 2917 (void) RelinquishUniqueFileResource(output_filename); 2918 } 2919 { 2920 #if defined(MAGICKCORE_RSVG_DELEGATE) 2921 #if defined(MAGICKCORE_CAIRO_DELEGATE) 2922 cairo_surface_t 2923 *cairo_surface; 2924 2925 cairo_t 2926 *cairo_image; 2927 2928 MemoryInfo 2929 *pixel_info; 2930 2931 register unsigned char 2932 *p; 2933 2934 RsvgDimensionData 2935 dimension_info; 2936 2937 unsigned char 2938 *pixels; 2939 2940 #else 2941 GdkPixbuf 2942 *pixel_buffer; 2943 2944 register const guchar 2945 *p; 2946 #endif 2947 2948 GError 2949 *error; 2950 2951 PixelInfo 2952 fill_color; 2953 2954 register ssize_t 2955 x; 2956 2957 register Quantum 2958 *q; 2959 2960 RsvgHandle 2961 *svg_handle; 2962 2963 ssize_t 2964 y; 2965 2966 svg_handle=rsvg_handle_new(); 2967 if (svg_handle == (RsvgHandle *) NULL) 2968 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 2969 rsvg_handle_set_base_uri(svg_handle,image_info->filename); 2970 if ((image->resolution.x != 90.0) && (image->resolution.y != 90.0)) 2971 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x, 2972 image->resolution.y); 2973 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0) 2974 { 2975 message[n]='\0'; 2976 error=(GError *) NULL; 2977 (void) rsvg_handle_write(svg_handle,message,n,&error); 2978 if (error != (GError *) NULL) 2979 g_error_free(error); 2980 } 2981 error=(GError *) NULL; 2982 rsvg_handle_close(svg_handle,&error); 2983 if (error != (GError *) NULL) 2984 g_error_free(error); 2985 #if defined(MAGICKCORE_CAIRO_DELEGATE) 2986 rsvg_handle_get_dimensions(svg_handle,&dimension_info); 2987 if (image_info->size != (char *) NULL) 2988 { 2989 (void) GetGeometry(image_info->size,(ssize_t *) NULL, 2990 (ssize_t *) NULL,&image->columns,&image->rows); 2991 if ((image->columns != 0) || (image->rows != 0)) 2992 { 2993 image->resolution.x=90.0*image->columns/dimension_info.width; 2994 image->resolution.y=90.0*image->rows/dimension_info.height; 2995 if (image->resolution.x == 0) 2996 image->resolution.x=image->resolution.y; 2997 else if (image->resolution.y == 0) 2998 image->resolution.y=image->resolution.x; 2999 else 3000 image->resolution.x=image->resolution.y=MagickMin( 3001 image->resolution.x,image->resolution.y); 3002 } 3003 } 3004 image->columns=image->resolution.x*dimension_info.width/90.0; 3005 image->rows=image->resolution.y*dimension_info.height/90.0; 3006 pixel_info=(MemoryInfo *) NULL; 3007 #else 3008 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle); 3009 rsvg_handle_free(svg_handle); 3010 image->columns=gdk_pixbuf_get_width(pixel_buffer); 3011 image->rows=gdk_pixbuf_get_height(pixel_buffer); 3012 #endif 3013 image->alpha_trait=BlendPixelTrait; 3014 SetImageProperty(image,"svg:base-uri", 3015 rsvg_handle_get_base_uri(svg_handle),exception); 3016 status=SetImageExtent(image,image->columns,image->rows,exception); 3017 if (status == MagickFalse) 3018 { 3019 #if !defined(MAGICKCORE_CAIRO_DELEGATE) 3020 g_object_unref(G_OBJECT(pixel_buffer)); 3021 #endif 3022 g_object_unref(svg_handle); 3023 ThrowReaderException(MissingDelegateError, 3024 "NoDecodeDelegateForThisImageFormat"); 3025 } 3026 if (image_info->ping == MagickFalse) 3027 { 3028 #if defined(MAGICKCORE_CAIRO_DELEGATE) 3029 size_t 3030 stride; 3031 3032 stride=4*image->columns; 3033 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE) 3034 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, 3035 (int) image->columns); 3036 #endif 3037 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels)); 3038 if (pixel_info == (MemoryInfo *) NULL) 3039 { 3040 g_object_unref(svg_handle); 3041 ThrowReaderException(ResourceLimitError, 3042 "MemoryAllocationFailed"); 3043 } 3044 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info); 3045 #endif 3046 (void) SetImageBackgroundColor(image,exception); 3047 #if defined(MAGICKCORE_CAIRO_DELEGATE) 3048 cairo_surface=cairo_image_surface_create_for_data(pixels, 3049 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int) 3050 stride); 3051 if (cairo_surface == (cairo_surface_t *) NULL) 3052 { 3053 pixel_info=RelinquishVirtualMemory(pixel_info); 3054 g_object_unref(svg_handle); 3055 ThrowReaderException(ResourceLimitError, 3056 "MemoryAllocationFailed"); 3057 } 3058 cairo_image=cairo_create(cairo_surface); 3059 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR); 3060 cairo_paint(cairo_image); 3061 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER); 3062 cairo_scale(cairo_image,image->resolution.x/90.0, 3063 image->resolution.y/90.0); 3064 rsvg_handle_render_cairo(svg_handle,cairo_image); 3065 cairo_destroy(cairo_image); 3066 cairo_surface_destroy(cairo_surface); 3067 g_object_unref(svg_handle); 3068 p=pixels; 3069 #else 3070 p=gdk_pixbuf_get_pixels(pixel_buffer); 3071 #endif 3072 GetPixelInfo(image,&fill_color); 3073 for (y=0; y < (ssize_t) image->rows; y++) 3074 { 3075 q=GetAuthenticPixels(image,0,y,image->columns,1,exception); 3076 if (q == (Quantum *) NULL) 3077 break; 3078 for (x=0; x < (ssize_t) image->columns; x++) 3079 { 3080 #if defined(MAGICKCORE_CAIRO_DELEGATE) 3081 fill_color.blue=ScaleCharToQuantum(*p++); 3082 fill_color.green=ScaleCharToQuantum(*p++); 3083 fill_color.red=ScaleCharToQuantum(*p++); 3084 #else 3085 fill_color.red=ScaleCharToQuantum(*p++); 3086 fill_color.green=ScaleCharToQuantum(*p++); 3087 fill_color.blue=ScaleCharToQuantum(*p++); 3088 #endif 3089 fill_color.alpha=ScaleCharToQuantum(*p++); 3090 #if defined(MAGICKCORE_CAIRO_DELEGATE) 3091 { 3092 double 3093 gamma; 3094 3095 gamma=QuantumScale*fill_color.alpha; 3096 gamma=PerceptibleReciprocal(gamma); 3097 fill_color.blue*=gamma; 3098 fill_color.green*=gamma; 3099 fill_color.red*=gamma; 3100 } 3101 #endif 3102 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double) 3103 GetPixelAlpha(image,q),q); 3104 q+=GetPixelChannels(image); 3105 } 3106 if (SyncAuthenticPixels(image,exception) == MagickFalse) 3107 break; 3108 if (image->previous == (Image *) NULL) 3109 { 3110 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) 3111 y,image->rows); 3112 if (status == MagickFalse) 3113 break; 3114 } 3115 } 3116 } 3117 #if defined(MAGICKCORE_CAIRO_DELEGATE) 3118 if (pixel_info != (MemoryInfo *) NULL) 3119 pixel_info=RelinquishVirtualMemory(pixel_info); 3120 #else 3121 g_object_unref(G_OBJECT(pixel_buffer)); 3122 #endif 3123 (void) CloseBlob(image); 3124 return(GetFirstImageInList(image)); 3125 #endif 3126 } 3127 } 3128 /* 3129 Open draw file. 3130 */ 3131 file=(FILE *) NULL; 3132 unique_file=AcquireUniqueFileResource(filename); 3133 if (unique_file != -1) 3134 file=fdopen(unique_file,"w"); 3135 if ((unique_file == -1) || (file == (FILE *) NULL)) 3136 { 3137 (void) CopyMagickString(image->filename,filename,MagickPathExtent); 3138 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile", 3139 image->filename); 3140 image=DestroyImageList(image); 3141 return((Image *) NULL); 3142 } 3143 /* 3144 Parse SVG file. 3145 */ 3146 svg_info=AcquireSVGInfo(); 3147 if (svg_info == (SVGInfo *) NULL) 3148 { 3149 (void) fclose(file); 3150 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 3151 } 3152 svg_info->file=file; 3153 svg_info->exception=exception; 3154 svg_info->image=image; 3155 svg_info->image_info=image_info; 3156 svg_info->bounds.width=image->columns; 3157 svg_info->bounds.height=image->rows; 3158 if (image_info->size != (char *) NULL) 3159 (void) CloneString(&svg_info->size,image_info->size); 3160 if (image->debug != MagickFalse) 3161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX"); 3162 (void) xmlSubstituteEntitiesDefault(1); 3163 (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules)); 3164 sax_modules.internalSubset=SVGInternalSubset; 3165 sax_modules.isStandalone=SVGIsStandalone; 3166 sax_modules.hasInternalSubset=SVGHasInternalSubset; 3167 sax_modules.hasExternalSubset=SVGHasExternalSubset; 3168 sax_modules.resolveEntity=SVGResolveEntity; 3169 sax_modules.getEntity=SVGGetEntity; 3170 sax_modules.entityDecl=SVGEntityDeclaration; 3171 sax_modules.notationDecl=SVGNotationDeclaration; 3172 sax_modules.attributeDecl=SVGAttributeDeclaration; 3173 sax_modules.elementDecl=SVGElementDeclaration; 3174 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration; 3175 sax_modules.setDocumentLocator=SVGSetDocumentLocator; 3176 sax_modules.startDocument=SVGStartDocument; 3177 sax_modules.endDocument=SVGEndDocument; 3178 sax_modules.startElement=SVGStartElement; 3179 sax_modules.endElement=SVGEndElement; 3180 sax_modules.reference=SVGReference; 3181 sax_modules.characters=SVGCharacters; 3182 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace; 3183 sax_modules.processingInstruction=SVGProcessingInstructions; 3184 sax_modules.comment=SVGComment; 3185 sax_modules.warning=SVGWarning; 3186 sax_modules.error=SVGError; 3187 sax_modules.fatalError=SVGError; 3188 sax_modules.getParameterEntity=SVGGetParameterEntity; 3189 sax_modules.cdataBlock=SVGCDataBlock; 3190 sax_modules.externalSubset=SVGExternalSubset; 3191 sax_handler=(&sax_modules); 3192 n=ReadBlob(image,MagickPathExtent-1,message); 3193 message[n]='\0'; 3194 if (n > 0) 3195 { 3196 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *) 3197 message,n,image->filename); 3198 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0) 3199 { 3200 message[n]='\0'; 3201 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0); 3202 if (status != 0) 3203 break; 3204 } 3205 } 3206 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1); 3207 SVGEndDocument(svg_info); 3208 xmlFreeParserCtxt(svg_info->parser); 3209 if (image->debug != MagickFalse) 3210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX"); 3211 (void) fclose(file); 3212 (void) CloseBlob(image); 3213 image->columns=svg_info->width; 3214 image->rows=svg_info->height; 3215 if (exception->severity >= ErrorException) 3216 { 3217 image=DestroyImage(image); 3218 return((Image *) NULL); 3219 } 3220 if (image_info->ping == MagickFalse) 3221 { 3222 ImageInfo 3223 *read_info; 3224 3225 /* 3226 Draw image. 3227 */ 3228 image=DestroyImage(image); 3229 image=(Image *) NULL; 3230 read_info=CloneImageInfo(image_info); 3231 SetImageInfoBlob(read_info,(void *) NULL,0); 3232 if (read_info->density != (char *) NULL) 3233 read_info->density=DestroyString(read_info->density); 3234 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s", 3235 filename); 3236 image=ReadImage(read_info,exception); 3237 read_info=DestroyImageInfo(read_info); 3238 if (image != (Image *) NULL) 3239 (void) CopyMagickString(image->filename,image_info->filename, 3240 MagickPathExtent); 3241 } 3242 /* 3243 Relinquish resources. 3244 */ 3245 if (image != (Image *) NULL) 3246 { 3247 if (svg_info->title != (char *) NULL) 3248 (void) SetImageProperty(image,"svg:title",svg_info->title,exception); 3249 if (svg_info->comment != (char *) NULL) 3250 (void) SetImageProperty(image,"svg:comment",svg_info->comment, 3251 exception); 3252 } 3253 svg_info=DestroySVGInfo(svg_info); 3254 (void) RelinquishUniqueFileResource(filename); 3255 return(GetFirstImageInList(image)); 3256 } 3257 #endif 3258 3259 /* 3261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3262 % % 3263 % % 3264 % % 3265 % R e g i s t e r S V G I m a g e % 3266 % % 3267 % % 3268 % % 3269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3270 % 3271 % RegisterSVGImage() adds attributes for the SVG image format to 3272 % the list of supported formats. The attributes include the image format 3273 % tag, a method to read and/or write the format, whether the format 3274 % supports the saving of more than one frame to the same file or blob, 3275 % whether the format supports native in-memory I/O, and a brief 3276 % description of the format. 3277 % 3278 % The format of the RegisterSVGImage method is: 3279 % 3280 % size_t RegisterSVGImage(void) 3281 % 3282 */ 3283 ModuleExport size_t RegisterSVGImage(void) 3284 { 3285 char 3286 version[MagickPathExtent]; 3287 3288 MagickInfo 3289 *entry; 3290 3291 *version='\0'; 3292 #if defined(LIBXML_DOTTED_VERSION) 3293 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION, 3294 MagickPathExtent); 3295 #endif 3296 #if defined(MAGICKCORE_RSVG_DELEGATE) 3297 #if !GLIB_CHECK_VERSION(2,35,0) 3298 g_type_init(); 3299 #endif 3300 #if defined(MAGICKCORE_XML_DELEGATE) 3301 xmlInitParser(); 3302 #endif 3303 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d", 3304 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION); 3305 #endif 3306 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics"); 3307 #if defined(MAGICKCORE_XML_DELEGATE) 3308 entry->decoder=(DecodeImageHandler *) ReadSVGImage; 3309 #endif 3310 entry->encoder=(EncodeImageHandler *) WriteSVGImage; 3311 entry->flags^=CoderBlobSupportFlag; 3312 entry->mime_type=ConstantString("image/svg+xml"); 3313 if (*version != '\0') 3314 entry->version=ConstantString(version); 3315 entry->magick=(IsImageFormatHandler *) IsSVG; 3316 (void) RegisterMagickInfo(entry); 3317 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics"); 3318 #if defined(MAGICKCORE_XML_DELEGATE) 3319 entry->decoder=(DecodeImageHandler *) ReadSVGImage; 3320 #endif 3321 entry->encoder=(EncodeImageHandler *) WriteSVGImage; 3322 entry->flags^=CoderBlobSupportFlag; 3323 entry->mime_type=ConstantString("image/svg+xml"); 3324 if (*version != '\0') 3325 entry->version=ConstantString(version); 3326 entry->magick=(IsImageFormatHandler *) IsSVG; 3327 (void) RegisterMagickInfo(entry); 3328 entry=AcquireMagickInfo("SVG","MSVG", 3329 "ImageMagick's own SVG internal renderer"); 3330 #if defined(MAGICKCORE_XML_DELEGATE) 3331 entry->decoder=(DecodeImageHandler *) ReadSVGImage; 3332 #endif 3333 entry->encoder=(EncodeImageHandler *) WriteSVGImage; 3334 entry->flags^=CoderBlobSupportFlag; 3335 entry->magick=(IsImageFormatHandler *) IsSVG; 3336 (void) RegisterMagickInfo(entry); 3337 return(MagickImageCoderSignature); 3338 } 3339 3340 /* 3342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3343 % % 3344 % % 3345 % % 3346 % U n r e g i s t e r S V G I m a g e % 3347 % % 3348 % % 3349 % % 3350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3351 % 3352 % UnregisterSVGImage() removes format registrations made by the 3353 % SVG module from the list of supported formats. 3354 % 3355 % The format of the UnregisterSVGImage method is: 3356 % 3357 % UnregisterSVGImage(void) 3358 % 3359 */ 3360 ModuleExport void UnregisterSVGImage(void) 3361 { 3362 (void) UnregisterMagickInfo("SVGZ"); 3363 (void) UnregisterMagickInfo("SVG"); 3364 (void) UnregisterMagickInfo("MSVG"); 3365 #if defined(MAGICKCORE_XML_DELEGATE) 3366 xmlCleanupParser(); 3367 #endif 3368 } 3369 3370 /* 3372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3373 % % 3374 % % 3375 % % 3376 % W r i t e S V G I m a g e % 3377 % % 3378 % % 3379 % % 3380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3381 % 3382 % WriteSVGImage() writes a image in the SVG - XML based W3C standard 3383 % format. 3384 % 3385 % The format of the WriteSVGImage method is: 3386 % 3387 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info, 3388 % Image *image,ExceptionInfo *exception) 3389 % 3390 % A description of each parameter follows. 3391 % 3392 % o image_info: the image info. 3393 % 3394 % o image: The image. 3395 % 3396 % o exception: return any errors or warnings in this structure. 3397 % 3398 */ 3399 3400 static void AffineToTransform(Image *image,AffineMatrix *affine) 3401 { 3402 char 3403 transform[MagickPathExtent]; 3404 3405 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon)) 3406 { 3407 if ((fabs(affine->rx) < MagickEpsilon) && 3408 (fabs(affine->ry) < MagickEpsilon)) 3409 { 3410 if ((fabs(affine->sx-1.0) < MagickEpsilon) && 3411 (fabs(affine->sy-1.0) < MagickEpsilon)) 3412 { 3413 (void) WriteBlobString(image,"\">\n"); 3414 return; 3415 } 3416 (void) FormatLocaleString(transform,MagickPathExtent, 3417 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy); 3418 (void) WriteBlobString(image,transform); 3419 return; 3420 } 3421 else 3422 { 3423 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) && 3424 (fabs(affine->rx+affine->ry) < MagickEpsilon) && 3425 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) < 3426 2*MagickEpsilon)) 3427 { 3428 double 3429 theta; 3430 3431 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx); 3432 (void) FormatLocaleString(transform,MagickPathExtent, 3433 "\" transform=\"rotate(%g)\">\n",theta); 3434 (void) WriteBlobString(image,transform); 3435 return; 3436 } 3437 } 3438 } 3439 else 3440 { 3441 if ((fabs(affine->sx-1.0) < MagickEpsilon) && 3442 (fabs(affine->rx) < MagickEpsilon) && 3443 (fabs(affine->ry) < MagickEpsilon) && 3444 (fabs(affine->sy-1.0) < MagickEpsilon)) 3445 { 3446 (void) FormatLocaleString(transform,MagickPathExtent, 3447 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty); 3448 (void) WriteBlobString(image,transform); 3449 return; 3450 } 3451 } 3452 (void) FormatLocaleString(transform,MagickPathExtent, 3453 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n", 3454 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty); 3455 (void) WriteBlobString(image,transform); 3456 } 3457 3458 static MagickBooleanType IsPoint(const char *point) 3459 { 3460 char 3461 *p; 3462 3463 ssize_t 3464 value; 3465 3466 value=strtol(point,&p,10); 3467 (void) value; 3468 return(p != point ? MagickTrue : MagickFalse); 3469 } 3470 3471 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception) 3472 { 3473 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE) 3474 { 3475 at_bitmap_type 3476 *trace; 3477 3478 at_fitting_opts_type 3479 *fitting_options; 3480 3481 at_output_opts_type 3482 *output_options; 3483 3484 at_splines_type 3485 *splines; 3486 3487 ImageType 3488 type; 3489 3490 register const PixelPacket 3491 *p; 3492 3493 register ssize_t 3494 i, 3495 x; 3496 3497 size_t 3498 number_planes; 3499 3500 ssize_t 3501 y; 3502 3503 /* 3504 Trace image and write as SVG. 3505 */ 3506 fitting_options=at_fitting_opts_new(); 3507 output_options=at_output_opts_new(); 3508 (void) SetImageGray(image,exception); 3509 type=GetImageType(image); 3510 number_planes=3; 3511 if ((type == BilevelType) || (type == GrayscaleType)) 3512 number_planes=1; 3513 trace=at_bitmap_new(image->columns,image->rows,number_planes); 3514 i=0; 3515 for (y=0; y < (ssize_t) image->rows; y++) 3516 { 3517 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 3518 if (p == (const PixelPacket *) NULL) 3519 break; 3520 for (x=0; x < (ssize_t) image->columns; x++) 3521 { 3522 trace->bitmap[i++]=GetPixelRed(image,p); 3523 if (number_planes == 3) 3524 { 3525 trace->bitmap[i++]=GetPixelGreen(image,p); 3526 trace->bitmap[i++]=GetPixelBlue(image,p); 3527 } 3528 p+=GetPixelChannels(image); 3529 } 3530 } 3531 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL, 3532 NULL); 3533 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"), 3534 GetBlobFileHandle(image),image->filename,output_options,splines,NULL, 3535 NULL); 3536 /* 3537 Free resources. 3538 */ 3539 at_splines_free(splines); 3540 at_bitmap_free(trace); 3541 at_output_opts_free(output_options); 3542 at_fitting_opts_free(fitting_options); 3543 } 3544 #else 3545 { 3546 char 3547 *base64, 3548 message[MagickPathExtent]; 3549 3550 Image 3551 *clone_image; 3552 3553 ImageInfo 3554 *image_info; 3555 3556 register char 3557 *p; 3558 3559 size_t 3560 blob_length, 3561 encode_length; 3562 3563 ssize_t 3564 i; 3565 3566 unsigned char 3567 *blob; 3568 3569 (void) WriteBlobString(image, 3570 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); 3571 (void) WriteBlobString(image, 3572 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\""); 3573 (void) WriteBlobString(image, 3574 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); 3575 (void) FormatLocaleString(message,MagickPathExtent, 3576 "<svg version=\"1.1\" id=\"Layer_1\" " 3577 "xmlns=\"http://www.w3.org/2000/svg\" " 3578 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" " 3579 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" " 3580 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">", 3581 (double) image->columns,(double) image->rows, 3582 (double) image->columns,(double) image->rows, 3583 (double) image->columns,(double) image->rows); 3584 (void) WriteBlobString(image,message); 3585 clone_image=CloneImage(image,0,0,MagickTrue,exception); 3586 if (clone_image == (Image *) NULL) 3587 return(MagickFalse); 3588 image_info=AcquireImageInfo(); 3589 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent); 3590 blob_length=2048; 3591 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length, 3592 exception); 3593 clone_image=DestroyImage(clone_image); 3594 image_info=DestroyImageInfo(image_info); 3595 if (blob == (unsigned char *) NULL) 3596 return(MagickFalse); 3597 encode_length=0; 3598 base64=Base64Encode(blob,blob_length,&encode_length); 3599 blob=(unsigned char *) RelinquishMagickMemory(blob); 3600 (void) FormatLocaleString(message,MagickPathExtent, 3601 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" " 3602 "x=\"%.20g\" y=\"%.20g\"\n xlink:href=\"data:image/png;base64,", 3603 (double) image->scene,(double) image->columns,(double) image->rows, 3604 (double) image->page.x,(double) image->page.y); 3605 (void) WriteBlobString(image,message); 3606 p=base64; 3607 for (i=(ssize_t) encode_length; i > 0; i-=76) 3608 { 3609 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p); 3610 (void) WriteBlobString(image,message); 3611 p+=76; 3612 if (i > 76) 3613 (void) WriteBlobString(image,"\n"); 3614 } 3615 base64=DestroyString(base64); 3616 (void) WriteBlobString(image,"\" />\n"); 3617 (void) WriteBlobString(image,"</svg>\n"); 3618 } 3619 #endif 3620 CloseBlob(image); 3621 return(MagickTrue); 3622 } 3623 3624 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image, 3625 ExceptionInfo *exception) 3626 { 3627 #define BezierQuantum 200 3628 3629 AffineMatrix 3630 affine; 3631 3632 char 3633 keyword[MagickPathExtent], 3634 message[MagickPathExtent], 3635 name[MagickPathExtent], 3636 *next_token, 3637 *token, 3638 type[MagickPathExtent]; 3639 3640 const char 3641 *p, 3642 *q, 3643 *value; 3644 3645 int 3646 n; 3647 3648 ssize_t 3649 j; 3650 3651 MagickBooleanType 3652 active, 3653 status; 3654 3655 PointInfo 3656 point; 3657 3658 PrimitiveInfo 3659 *primitive_info; 3660 3661 PrimitiveType 3662 primitive_type; 3663 3664 register ssize_t 3665 x; 3666 3667 register ssize_t 3668 i; 3669 3670 size_t 3671 extent, 3672 length, 3673 number_points; 3674 3675 SVGInfo 3676 svg_info; 3677 3678 /* 3679 Open output image file. 3680 */ 3681 assert(image_info != (const ImageInfo *) NULL); 3682 assert(image_info->signature == MagickCoreSignature); 3683 assert(image != (Image *) NULL); 3684 assert(image->signature == MagickCoreSignature); 3685 if (image->debug != MagickFalse) 3686 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3687 assert(exception != (ExceptionInfo *) NULL); 3688 assert(exception->signature == MagickCoreSignature); 3689 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 3690 if (status == MagickFalse) 3691 return(status); 3692 value=GetImageArtifact(image,"SVG"); 3693 if (value != (char *) NULL) 3694 { 3695 (void) WriteBlobString(image,value); 3696 (void) CloseBlob(image); 3697 return(MagickTrue); 3698 } 3699 value=GetImageArtifact(image,"MVG"); 3700 if (value == (char *) NULL) 3701 return(TraceSVGImage(image,exception)); 3702 /* 3703 Write SVG header. 3704 */ 3705 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n"); 3706 (void) WriteBlobString(image, 3707 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n"); 3708 (void) WriteBlobString(image, 3709 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"); 3710 (void) FormatLocaleString(message,MagickPathExtent, 3711 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double) 3712 image->rows); 3713 (void) WriteBlobString(image,message); 3714 /* 3715 Allocate primitive info memory. 3716 */ 3717 number_points=2047; 3718 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points, 3719 sizeof(*primitive_info)); 3720 if (primitive_info == (PrimitiveInfo *) NULL) 3721 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 3722 GetAffineMatrix(&affine); 3723 token=AcquireString(value); 3724 extent=strlen(token)+MagickPathExtent; 3725 active=MagickFalse; 3726 n=0; 3727 status=MagickTrue; 3728 for (q=(const char *) value; *q != '\0'; ) 3729 { 3730 /* 3731 Interpret graphic primitive. 3732 */ 3733 GetNextToken(q,&q,MagickPathExtent,keyword); 3734 if (*keyword == '\0') 3735 break; 3736 if (*keyword == '#') 3737 { 3738 /* 3739 Comment. 3740 */ 3741 if (active != MagickFalse) 3742 { 3743 AffineToTransform(image,&affine); 3744 active=MagickFalse; 3745 } 3746 (void) WriteBlobString(image,"<desc>"); 3747 (void) WriteBlobString(image,keyword+1); 3748 for ( ; (*q != '\n') && (*q != '\0'); q++) 3749 switch (*q) 3750 { 3751 case '<': (void) WriteBlobString(image,"<"); break; 3752 case '>': (void) WriteBlobString(image,">"); break; 3753 case '&': (void) WriteBlobString(image,"&"); break; 3754 default: (void) WriteBlobByte(image,*q); break; 3755 } 3756 (void) WriteBlobString(image,"</desc>\n"); 3757 continue; 3758 } 3759 primitive_type=UndefinedPrimitive; 3760 switch (*keyword) 3761 { 3762 case ';': 3763 break; 3764 case 'a': 3765 case 'A': 3766 { 3767 if (LocaleCompare("affine",keyword) == 0) 3768 { 3769 GetNextToken(q,&q,extent,token); 3770 affine.sx=StringToDouble(token,&next_token); 3771 GetNextToken(q,&q,extent,token); 3772 if (*token == ',') 3773 GetNextToken(q,&q,extent,token); 3774 affine.rx=StringToDouble(token,&next_token); 3775 GetNextToken(q,&q,extent,token); 3776 if (*token == ',') 3777 GetNextToken(q,&q,extent,token); 3778 affine.ry=StringToDouble(token,&next_token); 3779 GetNextToken(q,&q,extent,token); 3780 if (*token == ',') 3781 GetNextToken(q,&q,extent,token); 3782 affine.sy=StringToDouble(token,&next_token); 3783 GetNextToken(q,&q,extent,token); 3784 if (*token == ',') 3785 GetNextToken(q,&q,extent,token); 3786 affine.tx=StringToDouble(token,&next_token); 3787 GetNextToken(q,&q,extent,token); 3788 if (*token == ',') 3789 GetNextToken(q,&q,extent,token); 3790 affine.ty=StringToDouble(token,&next_token); 3791 break; 3792 } 3793 if (LocaleCompare("alpha",keyword) == 0) 3794 { 3795 primitive_type=AlphaPrimitive; 3796 break; 3797 } 3798 if (LocaleCompare("angle",keyword) == 0) 3799 { 3800 GetNextToken(q,&q,extent,token); 3801 affine.rx=StringToDouble(token,&next_token); 3802 affine.ry=StringToDouble(token,&next_token); 3803 break; 3804 } 3805 if (LocaleCompare("arc",keyword) == 0) 3806 { 3807 primitive_type=ArcPrimitive; 3808 break; 3809 } 3810 status=MagickFalse; 3811 break; 3812 } 3813 case 'b': 3814 case 'B': 3815 { 3816 if (LocaleCompare("bezier",keyword) == 0) 3817 { 3818 primitive_type=BezierPrimitive; 3819 break; 3820 } 3821 status=MagickFalse; 3822 break; 3823 } 3824 case 'c': 3825 case 'C': 3826 { 3827 if (LocaleCompare("clip-path",keyword) == 0) 3828 { 3829 GetNextToken(q,&q,extent,token); 3830 (void) FormatLocaleString(message,MagickPathExtent, 3831 "clip-path:url(#%s);",token); 3832 (void) WriteBlobString(image,message); 3833 break; 3834 } 3835 if (LocaleCompare("clip-rule",keyword) == 0) 3836 { 3837 GetNextToken(q,&q,extent,token); 3838 (void) FormatLocaleString(message,MagickPathExtent, 3839 "clip-rule:%s;",token); 3840 (void) WriteBlobString(image,message); 3841 break; 3842 } 3843 if (LocaleCompare("clip-units",keyword) == 0) 3844 { 3845 GetNextToken(q,&q,extent,token); 3846 (void) FormatLocaleString(message,MagickPathExtent, 3847 "clipPathUnits=%s;",token); 3848 (void) WriteBlobString(image,message); 3849 break; 3850 } 3851 if (LocaleCompare("circle",keyword) == 0) 3852 { 3853 primitive_type=CirclePrimitive; 3854 break; 3855 } 3856 if (LocaleCompare("color",keyword) == 0) 3857 { 3858 primitive_type=ColorPrimitive; 3859 break; 3860 } 3861 status=MagickFalse; 3862 break; 3863 } 3864 case 'd': 3865 case 'D': 3866 { 3867 if (LocaleCompare("decorate",keyword) == 0) 3868 { 3869 GetNextToken(q,&q,extent,token); 3870 (void) FormatLocaleString(message,MagickPathExtent, 3871 "text-decoration:%s;",token); 3872 (void) WriteBlobString(image,message); 3873 break; 3874 } 3875 status=MagickFalse; 3876 break; 3877 } 3878 case 'e': 3879 case 'E': 3880 { 3881 if (LocaleCompare("ellipse",keyword) == 0) 3882 { 3883 primitive_type=EllipsePrimitive; 3884 break; 3885 } 3886 status=MagickFalse; 3887 break; 3888 } 3889 case 'f': 3890 case 'F': 3891 { 3892 if (LocaleCompare("fill",keyword) == 0) 3893 { 3894 GetNextToken(q,&q,extent,token); 3895 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;", 3896 token); 3897 (void) WriteBlobString(image,message); 3898 break; 3899 } 3900 if (LocaleCompare("fill-rule",keyword) == 0) 3901 { 3902 GetNextToken(q,&q,extent,token); 3903 (void) FormatLocaleString(message,MagickPathExtent, 3904 "fill-rule:%s;",token); 3905 (void) WriteBlobString(image,message); 3906 break; 3907 } 3908 if (LocaleCompare("fill-alpha",keyword) == 0) 3909 { 3910 GetNextToken(q,&q,extent,token); 3911 (void) FormatLocaleString(message,MagickPathExtent, 3912 "fill-alpha:%s;",token); 3913 (void) WriteBlobString(image,message); 3914 break; 3915 } 3916 if (LocaleCompare("font-family",keyword) == 0) 3917 { 3918 GetNextToken(q,&q,extent,token); 3919 (void) FormatLocaleString(message,MagickPathExtent, 3920 "font-family:%s;",token); 3921 (void) WriteBlobString(image,message); 3922 break; 3923 } 3924 if (LocaleCompare("font-stretch",keyword) == 0) 3925 { 3926 GetNextToken(q,&q,extent,token); 3927 (void) FormatLocaleString(message,MagickPathExtent, 3928 "font-stretch:%s;",token); 3929 (void) WriteBlobString(image,message); 3930 break; 3931 } 3932 if (LocaleCompare("font-style",keyword) == 0) 3933 { 3934 GetNextToken(q,&q,extent,token); 3935 (void) FormatLocaleString(message,MagickPathExtent, 3936 "font-style:%s;",token); 3937 (void) WriteBlobString(image,message); 3938 break; 3939 } 3940 if (LocaleCompare("font-size",keyword) == 0) 3941 { 3942 GetNextToken(q,&q,extent,token); 3943 (void) FormatLocaleString(message,MagickPathExtent, 3944 "font-size:%s;",token); 3945 (void) WriteBlobString(image,message); 3946 break; 3947 } 3948 if (LocaleCompare("font-weight",keyword) == 0) 3949 { 3950 GetNextToken(q,&q,extent,token); 3951 (void) FormatLocaleString(message,MagickPathExtent, 3952 "font-weight:%s;",token); 3953 (void) WriteBlobString(image,message); 3954 break; 3955 } 3956 status=MagickFalse; 3957 break; 3958 } 3959 case 'g': 3960 case 'G': 3961 { 3962 if (LocaleCompare("gradient-units",keyword) == 0) 3963 { 3964 GetNextToken(q,&q,extent,token); 3965 break; 3966 } 3967 if (LocaleCompare("text-align",keyword) == 0) 3968 { 3969 GetNextToken(q,&q,extent,token); 3970 (void) FormatLocaleString(message,MagickPathExtent, 3971 "text-align %s ",token); 3972 (void) WriteBlobString(image,message); 3973 break; 3974 } 3975 if (LocaleCompare("text-anchor",keyword) == 0) 3976 { 3977 GetNextToken(q,&q,extent,token); 3978 (void) FormatLocaleString(message,MagickPathExtent, 3979 "text-anchor %s ",token); 3980 (void) WriteBlobString(image,message); 3981 break; 3982 } 3983 status=MagickFalse; 3984 break; 3985 } 3986 case 'i': 3987 case 'I': 3988 { 3989 if (LocaleCompare("image",keyword) == 0) 3990 { 3991 GetNextToken(q,&q,extent,token); 3992 primitive_type=ImagePrimitive; 3993 break; 3994 } 3995 status=MagickFalse; 3996 break; 3997 } 3998 case 'l': 3999 case 'L': 4000 { 4001 if (LocaleCompare("line",keyword) == 0) 4002 { 4003 primitive_type=LinePrimitive; 4004 break; 4005 } 4006 status=MagickFalse; 4007 break; 4008 } 4009 case 'o': 4010 case 'O': 4011 { 4012 if (LocaleCompare("opacity",keyword) == 0) 4013 { 4014 GetNextToken(q,&q,extent,token); 4015 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ", 4016 token); 4017 (void) WriteBlobString(image,message); 4018 break; 4019 } 4020 status=MagickFalse; 4021 break; 4022 } 4023 case 'p': 4024 case 'P': 4025 { 4026 if (LocaleCompare("path",keyword) == 0) 4027 { 4028 primitive_type=PathPrimitive; 4029 break; 4030 } 4031 if (LocaleCompare("point",keyword) == 0) 4032 { 4033 primitive_type=PointPrimitive; 4034 break; 4035 } 4036 if (LocaleCompare("polyline",keyword) == 0) 4037 { 4038 primitive_type=PolylinePrimitive; 4039 break; 4040 } 4041 if (LocaleCompare("polygon",keyword) == 0) 4042 { 4043 primitive_type=PolygonPrimitive; 4044 break; 4045 } 4046 if (LocaleCompare("pop",keyword) == 0) 4047 { 4048 GetNextToken(q,&q,extent,token); 4049 if (LocaleCompare("clip-path",token) == 0) 4050 { 4051 (void) WriteBlobString(image,"</clipPath>\n"); 4052 break; 4053 } 4054 if (LocaleCompare("defs",token) == 0) 4055 { 4056 (void) WriteBlobString(image,"</defs>\n"); 4057 break; 4058 } 4059 if (LocaleCompare("gradient",token) == 0) 4060 { 4061 (void) FormatLocaleString(message,MagickPathExtent, 4062 "</%sGradient>\n",type); 4063 (void) WriteBlobString(image,message); 4064 break; 4065 } 4066 if (LocaleCompare("graphic-context",token) == 0) 4067 { 4068 n--; 4069 if (n < 0) 4070 ThrowWriterException(DrawError, 4071 "UnbalancedGraphicContextPushPop"); 4072 (void) WriteBlobString(image,"</g>\n"); 4073 } 4074 if (LocaleCompare("pattern",token) == 0) 4075 { 4076 (void) WriteBlobString(image,"</pattern>\n"); 4077 break; 4078 } 4079 if (LocaleCompare("defs",token) == 0) 4080 (void) WriteBlobString(image,"</g>\n"); 4081 break; 4082 } 4083 if (LocaleCompare("push",keyword) == 0) 4084 { 4085 GetNextToken(q,&q,extent,token); 4086 if (LocaleCompare("clip-path",token) == 0) 4087 { 4088 GetNextToken(q,&q,extent,token); 4089 (void) FormatLocaleString(message,MagickPathExtent, 4090 "<clipPath id=\"%s\">\n",token); 4091 (void) WriteBlobString(image,message); 4092 break; 4093 } 4094 if (LocaleCompare("defs",token) == 0) 4095 { 4096 (void) WriteBlobString(image,"<defs>\n"); 4097 break; 4098 } 4099 if (LocaleCompare("gradient",token) == 0) 4100 { 4101 GetNextToken(q,&q,extent,token); 4102 (void) CopyMagickString(name,token,MagickPathExtent); 4103 GetNextToken(q,&q,extent,token); 4104 (void) CopyMagickString(type,token,MagickPathExtent); 4105 GetNextToken(q,&q,extent,token); 4106 svg_info.segment.x1=StringToDouble(token,&next_token); 4107 svg_info.element.cx=StringToDouble(token,&next_token); 4108 GetNextToken(q,&q,extent,token); 4109 if (*token == ',') 4110 GetNextToken(q,&q,extent,token); 4111 svg_info.segment.y1=StringToDouble(token,&next_token); 4112 svg_info.element.cy=StringToDouble(token,&next_token); 4113 GetNextToken(q,&q,extent,token); 4114 if (*token == ',') 4115 GetNextToken(q,&q,extent,token); 4116 svg_info.segment.x2=StringToDouble(token,&next_token); 4117 svg_info.element.major=StringToDouble(token, 4118 (char **) NULL); 4119 GetNextToken(q,&q,extent,token); 4120 if (*token == ',') 4121 GetNextToken(q,&q,extent,token); 4122 svg_info.segment.y2=StringToDouble(token,&next_token); 4123 svg_info.element.minor=StringToDouble(token, 4124 (char **) NULL); 4125 (void) FormatLocaleString(message,MagickPathExtent, 4126 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" " 4127 "y2=\"%g\">\n",type,name,svg_info.segment.x1, 4128 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2); 4129 if (LocaleCompare(type,"radial") == 0) 4130 { 4131 GetNextToken(q,&q,extent,token); 4132 if (*token == ',') 4133 GetNextToken(q,&q,extent,token); 4134 svg_info.element.angle=StringToDouble(token, 4135 (char **) NULL); 4136 (void) FormatLocaleString(message,MagickPathExtent, 4137 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" " 4138 "fx=\"%g\" fy=\"%g\">\n",type,name, 4139 svg_info.element.cx,svg_info.element.cy, 4140 svg_info.element.angle,svg_info.element.major, 4141 svg_info.element.minor); 4142 } 4143 (void) WriteBlobString(image,message); 4144 break; 4145 } 4146 if (LocaleCompare("graphic-context",token) == 0) 4147 { 4148 n++; 4149 if (active) 4150 { 4151 AffineToTransform(image,&affine); 4152 active=MagickFalse; 4153 } 4154 (void) WriteBlobString(image,"<g style=\""); 4155 active=MagickTrue; 4156 } 4157 if (LocaleCompare("pattern",token) == 0) 4158 { 4159 GetNextToken(q,&q,extent,token); 4160 (void) CopyMagickString(name,token,MagickPathExtent); 4161 GetNextToken(q,&q,extent,token); 4162 svg_info.bounds.x=StringToDouble(token,&next_token); 4163 GetNextToken(q,&q,extent,token); 4164 if (*token == ',') 4165 GetNextToken(q,&q,extent,token); 4166 svg_info.bounds.y=StringToDouble(token,&next_token); 4167 GetNextToken(q,&q,extent,token); 4168 if (*token == ',') 4169 GetNextToken(q,&q,extent,token); 4170 svg_info.bounds.width=StringToDouble(token, 4171 (char **) NULL); 4172 GetNextToken(q,&q,extent,token); 4173 if (*token == ',') 4174 GetNextToken(q,&q,extent,token); 4175 svg_info.bounds.height=StringToDouble(token,(char **) NULL); 4176 (void) FormatLocaleString(message,MagickPathExtent, 4177 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" " 4178 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y, 4179 svg_info.bounds.width,svg_info.bounds.height); 4180 (void) WriteBlobString(image,message); 4181 break; 4182 } 4183 break; 4184 } 4185 status=MagickFalse; 4186 break; 4187 } 4188 case 'r': 4189 case 'R': 4190 { 4191 if (LocaleCompare("rectangle",keyword) == 0) 4192 { 4193 primitive_type=RectanglePrimitive; 4194 break; 4195 } 4196 if (LocaleCompare("roundRectangle",keyword) == 0) 4197 { 4198 primitive_type=RoundRectanglePrimitive; 4199 break; 4200 } 4201 if (LocaleCompare("rotate",keyword) == 0) 4202 { 4203 GetNextToken(q,&q,extent,token); 4204 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ", 4205 token); 4206 (void) WriteBlobString(image,message); 4207 break; 4208 } 4209 status=MagickFalse; 4210 break; 4211 } 4212 case 's': 4213 case 'S': 4214 { 4215 if (LocaleCompare("scale",keyword) == 0) 4216 { 4217 GetNextToken(q,&q,extent,token); 4218 affine.sx=StringToDouble(token,&next_token); 4219 GetNextToken(q,&q,extent,token); 4220 if (*token == ',') 4221 GetNextToken(q,&q,extent,token); 4222 affine.sy=StringToDouble(token,&next_token); 4223 break; 4224 } 4225 if (LocaleCompare("skewX",keyword) == 0) 4226 { 4227 GetNextToken(q,&q,extent,token); 4228 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ", 4229 token); 4230 (void) WriteBlobString(image,message); 4231 break; 4232 } 4233 if (LocaleCompare("skewY",keyword) == 0) 4234 { 4235 GetNextToken(q,&q,extent,token); 4236 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ", 4237 token); 4238 (void) WriteBlobString(image,message); 4239 break; 4240 } 4241 if (LocaleCompare("stop-color",keyword) == 0) 4242 { 4243 char 4244 color[MagickPathExtent]; 4245 4246 GetNextToken(q,&q,extent,token); 4247 (void) CopyMagickString(color,token,MagickPathExtent); 4248 GetNextToken(q,&q,extent,token); 4249 (void) FormatLocaleString(message,MagickPathExtent, 4250 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color); 4251 (void) WriteBlobString(image,message); 4252 break; 4253 } 4254 if (LocaleCompare("stroke",keyword) == 0) 4255 { 4256 GetNextToken(q,&q,extent,token); 4257 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;", 4258 token); 4259 (void) WriteBlobString(image,message); 4260 break; 4261 } 4262 if (LocaleCompare("stroke-antialias",keyword) == 0) 4263 { 4264 GetNextToken(q,&q,extent,token); 4265 (void) FormatLocaleString(message,MagickPathExtent, 4266 "stroke-antialias:%s;",token); 4267 (void) WriteBlobString(image,message); 4268 break; 4269 } 4270 if (LocaleCompare("stroke-dasharray",keyword) == 0) 4271 { 4272 if (IsPoint(q)) 4273 { 4274 ssize_t 4275 k; 4276 4277 p=q; 4278 GetNextToken(p,&p,extent,token); 4279 for (k=0; IsPoint(token); k++) 4280 GetNextToken(p,&p,extent,token); 4281 (void) WriteBlobString(image,"stroke-dasharray:"); 4282 for (j=0; j < k; j++) 4283 { 4284 GetNextToken(q,&q,extent,token); 4285 (void) FormatLocaleString(message,MagickPathExtent,"%s ", 4286 token); 4287 (void) WriteBlobString(image,message); 4288 } 4289 (void) WriteBlobString(image,";"); 4290 break; 4291 } 4292 GetNextToken(q,&q,extent,token); 4293 (void) FormatLocaleString(message,MagickPathExtent, 4294 "stroke-dasharray:%s;",token); 4295 (void) WriteBlobString(image,message); 4296 break; 4297 } 4298 if (LocaleCompare("stroke-dashoffset",keyword) == 0) 4299 { 4300 GetNextToken(q,&q,extent,token); 4301 (void) FormatLocaleString(message,MagickPathExtent, 4302 "stroke-dashoffset:%s;",token); 4303 (void) WriteBlobString(image,message); 4304 break; 4305 } 4306 if (LocaleCompare("stroke-linecap",keyword) == 0) 4307 { 4308 GetNextToken(q,&q,extent,token); 4309 (void) FormatLocaleString(message,MagickPathExtent, 4310 "stroke-linecap:%s;",token); 4311 (void) WriteBlobString(image,message); 4312 break; 4313 } 4314 if (LocaleCompare("stroke-linejoin",keyword) == 0) 4315 { 4316 GetNextToken(q,&q,extent,token); 4317 (void) FormatLocaleString(message,MagickPathExtent, 4318 "stroke-linejoin:%s;",token); 4319 (void) WriteBlobString(image,message); 4320 break; 4321 } 4322 if (LocaleCompare("stroke-miterlimit",keyword) == 0) 4323 { 4324 GetNextToken(q,&q,extent,token); 4325 (void) FormatLocaleString(message,MagickPathExtent, 4326 "stroke-miterlimit:%s;",token); 4327 (void) WriteBlobString(image,message); 4328 break; 4329 } 4330 if (LocaleCompare("stroke-opacity",keyword) == 0) 4331 { 4332 GetNextToken(q,&q,extent,token); 4333 (void) FormatLocaleString(message,MagickPathExtent, 4334 "stroke-opacity:%s;",token); 4335 (void) WriteBlobString(image,message); 4336 break; 4337 } 4338 if (LocaleCompare("stroke-width",keyword) == 0) 4339 { 4340 GetNextToken(q,&q,extent,token); 4341 (void) FormatLocaleString(message,MagickPathExtent, 4342 "stroke-width:%s;",token); 4343 (void) WriteBlobString(image,message); 4344 continue; 4345 } 4346 status=MagickFalse; 4347 break; 4348 } 4349 case 't': 4350 case 'T': 4351 { 4352 if (LocaleCompare("text",keyword) == 0) 4353 { 4354 primitive_type=TextPrimitive; 4355 break; 4356 } 4357 if (LocaleCompare("text-antialias",keyword) == 0) 4358 { 4359 GetNextToken(q,&q,extent,token); 4360 (void) FormatLocaleString(message,MagickPathExtent, 4361 "text-antialias:%s;",token); 4362 (void) WriteBlobString(image,message); 4363 break; 4364 } 4365 if (LocaleCompare("tspan",keyword) == 0) 4366 { 4367 primitive_type=TextPrimitive; 4368 break; 4369 } 4370 if (LocaleCompare("translate",keyword) == 0) 4371 { 4372 GetNextToken(q,&q,extent,token); 4373 affine.tx=StringToDouble(token,&next_token); 4374 GetNextToken(q,&q,extent,token); 4375 if (*token == ',') 4376 GetNextToken(q,&q,extent,token); 4377 affine.ty=StringToDouble(token,&next_token); 4378 break; 4379 } 4380 status=MagickFalse; 4381 break; 4382 } 4383 case 'v': 4384 case 'V': 4385 { 4386 if (LocaleCompare("viewbox",keyword) == 0) 4387 { 4388 GetNextToken(q,&q,extent,token); 4389 if (*token == ',') 4390 GetNextToken(q,&q,extent,token); 4391 GetNextToken(q,&q,extent,token); 4392 if (*token == ',') 4393 GetNextToken(q,&q,extent,token); 4394 GetNextToken(q,&q,extent,token); 4395 if (*token == ',') 4396 GetNextToken(q,&q,extent,token); 4397 GetNextToken(q,&q,extent,token); 4398 break; 4399 } 4400 status=MagickFalse; 4401 break; 4402 } 4403 default: 4404 { 4405 status=MagickFalse; 4406 break; 4407 } 4408 } 4409 if (status == MagickFalse) 4410 break; 4411 if (primitive_type == UndefinedPrimitive) 4412 continue; 4413 /* 4414 Parse the primitive attributes. 4415 */ 4416 i=0; 4417 j=0; 4418 for (x=0; *q != '\0'; x++) 4419 { 4420 /* 4421 Define points. 4422 */ 4423 if (IsPoint(q) == MagickFalse) 4424 break; 4425 GetNextToken(q,&q,extent,token); 4426 point.x=StringToDouble(token,&next_token); 4427 GetNextToken(q,&q,extent,token); 4428 if (*token == ',') 4429 GetNextToken(q,&q,extent,token); 4430 point.y=StringToDouble(token,&next_token); 4431 GetNextToken(q,(const char **) NULL,extent,token); 4432 if (*token == ',') 4433 GetNextToken(q,&q,extent,token); 4434 primitive_info[i].primitive=primitive_type; 4435 primitive_info[i].point=point; 4436 primitive_info[i].coordinates=0; 4437 primitive_info[i].method=FloodfillMethod; 4438 i++; 4439 if (i < (ssize_t) (number_points-6*BezierQuantum-360)) 4440 continue; 4441 number_points+=6*BezierQuantum+360; 4442 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info, 4443 number_points,sizeof(*primitive_info)); 4444 if (primitive_info == (PrimitiveInfo *) NULL) 4445 { 4446 (void) ThrowMagickException(exception,GetMagickModule(), 4447 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 4448 break; 4449 } 4450 } 4451 primitive_info[j].primitive=primitive_type; 4452 primitive_info[j].coordinates=x; 4453 primitive_info[j].method=FloodfillMethod; 4454 primitive_info[j].text=(char *) NULL; 4455 if (active) 4456 { 4457 AffineToTransform(image,&affine); 4458 active=MagickFalse; 4459 } 4460 active=MagickFalse; 4461 switch (primitive_type) 4462 { 4463 case PointPrimitive: 4464 default: 4465 { 4466 if (primitive_info[j].coordinates != 1) 4467 { 4468 status=MagickFalse; 4469 break; 4470 } 4471 break; 4472 } 4473 case LinePrimitive: 4474 { 4475 if (primitive_info[j].coordinates != 2) 4476 { 4477 status=MagickFalse; 4478 break; 4479 } 4480 (void) FormatLocaleString(message,MagickPathExtent, 4481 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n", 4482 primitive_info[j].point.x,primitive_info[j].point.y, 4483 primitive_info[j+1].point.x,primitive_info[j+1].point.y); 4484 (void) WriteBlobString(image,message); 4485 break; 4486 } 4487 case RectanglePrimitive: 4488 { 4489 if (primitive_info[j].coordinates != 2) 4490 { 4491 status=MagickFalse; 4492 break; 4493 } 4494 (void) FormatLocaleString(message,MagickPathExtent, 4495 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n", 4496 primitive_info[j].point.x,primitive_info[j].point.y, 4497 primitive_info[j+1].point.x-primitive_info[j].point.x, 4498 primitive_info[j+1].point.y-primitive_info[j].point.y); 4499 (void) WriteBlobString(image,message); 4500 break; 4501 } 4502 case RoundRectanglePrimitive: 4503 { 4504 if (primitive_info[j].coordinates != 3) 4505 { 4506 status=MagickFalse; 4507 break; 4508 } 4509 (void) FormatLocaleString(message,MagickPathExtent, 4510 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" " 4511 "ry=\"%g\"/>\n",primitive_info[j].point.x, 4512 primitive_info[j].point.y,primitive_info[j+1].point.x- 4513 primitive_info[j].point.x,primitive_info[j+1].point.y- 4514 primitive_info[j].point.y,primitive_info[j+2].point.x, 4515 primitive_info[j+2].point.y); 4516 (void) WriteBlobString(image,message); 4517 break; 4518 } 4519 case ArcPrimitive: 4520 { 4521 if (primitive_info[j].coordinates != 3) 4522 { 4523 status=MagickFalse; 4524 break; 4525 } 4526 break; 4527 } 4528 case EllipsePrimitive: 4529 { 4530 if (primitive_info[j].coordinates != 3) 4531 { 4532 status=MagickFalse; 4533 break; 4534 } 4535 (void) FormatLocaleString(message,MagickPathExtent, 4536 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n", 4537 primitive_info[j].point.x,primitive_info[j].point.y, 4538 primitive_info[j+1].point.x,primitive_info[j+1].point.y); 4539 (void) WriteBlobString(image,message); 4540 break; 4541 } 4542 case CirclePrimitive: 4543 { 4544 double 4545 alpha, 4546 beta; 4547 4548 if (primitive_info[j].coordinates != 2) 4549 { 4550 status=MagickFalse; 4551 break; 4552 } 4553 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x; 4554 beta=primitive_info[j+1].point.y-primitive_info[j].point.y; 4555 (void) FormatLocaleString(message,MagickPathExtent, 4556 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n", 4557 primitive_info[j].point.x,primitive_info[j].point.y, 4558 hypot(alpha,beta)); 4559 (void) WriteBlobString(image,message); 4560 break; 4561 } 4562 case PolylinePrimitive: 4563 { 4564 if (primitive_info[j].coordinates < 2) 4565 { 4566 status=MagickFalse; 4567 break; 4568 } 4569 (void) CopyMagickString(message," <polyline points=\"", 4570 MagickPathExtent); 4571 (void) WriteBlobString(image,message); 4572 length=strlen(message); 4573 for ( ; j < i; j++) 4574 { 4575 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ", 4576 primitive_info[j].point.x,primitive_info[j].point.y); 4577 length+=strlen(message); 4578 if (length >= 80) 4579 { 4580 (void) WriteBlobString(image,"\n "); 4581 length=strlen(message)+5; 4582 } 4583 (void) WriteBlobString(image,message); 4584 } 4585 (void) WriteBlobString(image,"\"/>\n"); 4586 break; 4587 } 4588 case PolygonPrimitive: 4589 { 4590 if (primitive_info[j].coordinates < 3) 4591 { 4592 status=MagickFalse; 4593 break; 4594 } 4595 primitive_info[i]=primitive_info[j]; 4596 primitive_info[i].coordinates=0; 4597 primitive_info[j].coordinates++; 4598 i++; 4599 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent); 4600 (void) WriteBlobString(image,message); 4601 length=strlen(message); 4602 for ( ; j < i; j++) 4603 { 4604 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ", 4605 primitive_info[j].point.x,primitive_info[j].point.y); 4606 length+=strlen(message); 4607 if (length >= 80) 4608 { 4609 (void) WriteBlobString(image,"\n "); 4610 length=strlen(message)+5; 4611 } 4612 (void) WriteBlobString(image,message); 4613 } 4614 (void) WriteBlobString(image,"\"/>\n"); 4615 break; 4616 } 4617 case BezierPrimitive: 4618 { 4619 if (primitive_info[j].coordinates < 3) 4620 { 4621 status=MagickFalse; 4622 break; 4623 } 4624 break; 4625 } 4626 case PathPrimitive: 4627 { 4628 int 4629 number_attributes; 4630 4631 GetNextToken(q,&q,extent,token); 4632 number_attributes=1; 4633 for (p=token; *p != '\0'; p++) 4634 if (isalpha((int) *p)) 4635 number_attributes++; 4636 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1)) 4637 { 4638 number_points+=6*BezierQuantum*number_attributes; 4639 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info, 4640 number_points,sizeof(*primitive_info)); 4641 if (primitive_info == (PrimitiveInfo *) NULL) 4642 { 4643 (void) ThrowMagickException(exception,GetMagickModule(), 4644 ResourceLimitError,"MemoryAllocationFailed","`%s'", 4645 image->filename); 4646 break; 4647 } 4648 } 4649 (void) WriteBlobString(image," <path d=\""); 4650 (void) WriteBlobString(image,token); 4651 (void) WriteBlobString(image,"\"/>\n"); 4652 break; 4653 } 4654 case AlphaPrimitive: 4655 case ColorPrimitive: 4656 { 4657 if (primitive_info[j].coordinates != 1) 4658 { 4659 status=MagickFalse; 4660 break; 4661 } 4662 GetNextToken(q,&q,extent,token); 4663 if (LocaleCompare("point",token) == 0) 4664 primitive_info[j].method=PointMethod; 4665 if (LocaleCompare("replace",token) == 0) 4666 primitive_info[j].method=ReplaceMethod; 4667 if (LocaleCompare("floodfill",token) == 0) 4668 primitive_info[j].method=FloodfillMethod; 4669 if (LocaleCompare("filltoborder",token) == 0) 4670 primitive_info[j].method=FillToBorderMethod; 4671 if (LocaleCompare("reset",token) == 0) 4672 primitive_info[j].method=ResetMethod; 4673 break; 4674 } 4675 case TextPrimitive: 4676 { 4677 register char 4678 *p; 4679 4680 if (primitive_info[j].coordinates != 1) 4681 { 4682 status=MagickFalse; 4683 break; 4684 } 4685 GetNextToken(q,&q,extent,token); 4686 (void) FormatLocaleString(message,MagickPathExtent, 4687 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x, 4688 primitive_info[j].point.y); 4689 (void) WriteBlobString(image,message); 4690 for (p=token; *p != '\0'; p++) 4691 switch (*p) 4692 { 4693 case '<': (void) WriteBlobString(image,"<"); break; 4694 case '>': (void) WriteBlobString(image,">"); break; 4695 case '&': (void) WriteBlobString(image,"&"); break; 4696 default: (void) WriteBlobByte(image,*p); break; 4697 } 4698 (void) WriteBlobString(image,"</text>\n"); 4699 break; 4700 } 4701 case ImagePrimitive: 4702 { 4703 if (primitive_info[j].coordinates != 2) 4704 { 4705 status=MagickFalse; 4706 break; 4707 } 4708 GetNextToken(q,&q,extent,token); 4709 (void) FormatLocaleString(message,MagickPathExtent, 4710 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" " 4711 "xlink:href=\"%s\"/>\n",primitive_info[j].point.x, 4712 primitive_info[j].point.y,primitive_info[j+1].point.x, 4713 primitive_info[j+1].point.y,token); 4714 (void) WriteBlobString(image,message); 4715 break; 4716 } 4717 } 4718 if (primitive_info == (PrimitiveInfo *) NULL) 4719 break; 4720 primitive_info[i].primitive=UndefinedPrimitive; 4721 if (status == MagickFalse) 4722 break; 4723 } 4724 (void) WriteBlobString(image,"</svg>\n"); 4725 /* 4726 Relinquish resources. 4727 */ 4728 token=DestroyString(token); 4729 if (primitive_info != (PrimitiveInfo *) NULL) 4730 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info); 4731 (void) CloseBlob(image); 4732 return(status); 4733 } 4734