PageRenderTime 42ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/coders/svg.c

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