Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Suppose I have a Mathematica graphic such as

g = Graphics[{Disk[{-1, 0}, 1/2], Rectangle[]}]

square and circle

I would like to be able to export this graphic from Mathematica as a SVG file. I also want to set attributes for the SVG elements that represent the circle and the square. Is this possible?

I create my SVG file with

ExportString[g, "SVG"]
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
  <g transform='translate(-74.000000, -13.000000)'>
    <g transform='matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)' clip-path='url(#cp1)'>
      <clipPath id='cp1'>
        <polygon points='-2.2518e+15,-2.2518e+15 -2.2518e+15,2.2518e+15 2.2518e+15,2.2518e+15 2.2518e+15,-2.2518e+15 '/>
      </clipPath>
      <rect fill='rgb(100%,100%,100%)' stroke='none' x='74' y='13' width='360' height='222' />
    </g>
    <g transform='matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)' clip-path='url(#cp2)'>
      <clipPath id='cp2'>
        <polygon points='74,13 74,235 434,235 434,13 '/>
      </clipPath>
      <ellipse fill='rgb(0%,0%,0%)' opacity='1' stroke='none' cx='150' cy='158' rx='68.928' ry='68.928' />
      <polygon stroke='none' opacity='1' fill-rule='evenodd' fill='rgb(0%,0%,0%)' points='287.964,157.749 287.964,19.8928 425.82,19.8928 425.82,157.749 ' />
    </g>
  </g>
  <defs></defs>
</svg>

I would like the ability to insert an id attribute in the <polygon> and <ellipse> tags. I have already tried using the Annotation but that didn't produce any difference in the result.

My real application has many more graphical objects that I plan to control from JavaScript.

Preserving such information in the SVG file is essential for creating data-driven visualizations for the web using tools such as D3. Visualizations like this one are easier if we have a way to uniquely tag each region in the SVG.

share|improve this question
    
If there is not hidden option for this task, one possible solution is to save the object piece by piece and merge them to a single svg by yourself. Then you can control the id of the element. If we can access the source of Export, then there is another story to tell. Sadly, we cannot. –  Kattern Jun 5 at 14:27
    
What version of MMA are you using?. Following the same procedure my svg is quite different –  elbOlita Jun 5 at 23:44
    
@DiegoAliaga I suggest starting a package for this purpose, it is quite useful. –  Kattern Jun 6 at 11:55

1 Answer 1

up vote 5 down vote accepted
+500

First, let me tell you that following your code my output is quite different.

g=Graphics[{Disk[{-1,0},1/2],Rectangle[]}];
ExportString[g,"SVG"]

Output

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="361pt" height="223pt" viewBox="0 0 361 223" version="1.1">
<g id="surface499">
<path style="fill-rule:nonzero;fill:rgb(99.998474%,99.998474%,99.998474%);fill-opacity:1;stroke-width:0.03;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(99.998474%,99.998474%,99.998474%);stroke-opacity:1;stroke-miterlimit:10;" d="M 74 235 L 434 235 L 434 13 L 74 13 Z " transform="matrix(1,0,0,1,-74,-13)"/>
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 144.929688 145 C 144.929688 126.71875 137.664062 109.1875 124.738281 96.261719 C 111.8125 83.335938 94.28125 76.070312 76 76.070312 C 57.71875 76.070312 40.1875 83.335938 27.261719 96.261719 C 14.335938 109.1875 7.070312 126.71875 7.070312 145 C 7.070312 163.28125 14.335938 180.8125 27.261719 193.738281 C 40.1875 206.664062 57.71875 213.929688 76 213.929688 C 94.28125 213.929688 111.8125 206.664062 124.738281 193.738281 C 137.664062 180.8125 144.929688 163.28125 144.929688 145 Z "/>
<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.03;stroke-linecap:square;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:3.239307;" d="M 287.964844 157.75 L 287.964844 19.894531 L 425.820312 19.894531 L 425.820312 157.75 Z " transform="matrix(1,0,0,1,-74,-13)"/>
</g>
</svg>

But lets work with this output since I dont know what MMA version or OS you used to generate the other code. Mine is MMA 10.0.1, MacOS Yosemite. You could export each element separately and tag them using the Tooltip function. Then using patterns we add the id's

gra=
Graphics[
    {
    Tooltip[{Red,Disk[{2,1}]},"disk"],
    Tooltip[{Blue,Rectangle[],Polygon[{{-1,1},{-1,0},{0,0}}]} ,"rect"]
    }
,
PlotRange->{{-1,3},{0,2}}
]

enter image description here create an array with the graphic object and label from gra on each row.

individualObjectsAndTags=Cases[gra,Tooltip[object_,id_]->{object,id},\[Infinity]]

Output

{{{RGBColor[1, 0, 0], Disk[{2, 1}]}, disk}, {{RGBColor[0, 0, 1], Rectangle[{0, 0}],Polygon[{{-1, 1}, {-1, 0}, {0, 0}}]}, rect}}

Export every element as SVG object, then import as XML , replace the id tag and store every "g" element. It is important to define the plot range in Gra so that it can be used here as an option for the individual graphics.

graphicOptions=Sequence@@gra[[2;;]];
xmlElements=Module[{xml},

    xml=ImportString[ExportString[Graphics[#[[1]],graphicOptions],"SVG",Background->None],"XML"];
    Cases[xml,XMLElement["g",{"id"->__},b__]:>XMLElement["g",{"id"->#[[2]]},b],\[Infinity]]

]&/@individualObjectsAndTags//Flatten

Create another svg string and export gra. From this we obtain the wrapper for the g elements. Then replace the g element by the id g elements. Finally export as "XML"

wrapper=ImportString[ExportString[gra,"SVG"],"XML"];
(*exp=ReplaceAll[wrapper,{XMLElement["g",___]}->xmlElements]; error was here*)
exp = ReplaceAll[wrapper, XMLElement["g", ___] -> Sequence@@xmlElements];

ExportString[exp, "XML"]

Output

<?xml version='1.0' encoding='UTF-8'?>
<svg xmlns='http://www.w3.org/2000/svg'
    xmlns:xlink='http://www.w3.org/1999/xlink'
    width='361pt'
    height='181pt'
    viewBox='0 0 361 181'
    version='1.1'>
 <defs>
  <clipPath id='clip1'>
   <path d='M 89 89 L 181 89 L 181 180 L 89 180 Z ' />
  </clipPath>
  <clipPath id='clip2'>
   <path d='M 0 89 L 91 89 L 91 180 L 0 180 Z ' />
  </clipPath>
 </defs>
 <g id='disk'>
  <path style=' stroke:none;fill-rule:evenodd;fill:rgb(99.998474%,0%,0%);fill-opacity:1;'
      d='M 360 90 C 360 66.128906 350.519531 43.238281 333.640625 26.359375 C 316.761719 9.480469 293.871094 0 270 0 C 246.128906 0 223.238281 9.480469 206.359375 26.359375 C 189.480469 43.238281 180 66.128906 180 90 C 180 113.871094 189.480469 136.761719 206.359375 153.640625 C 223.238281 170.519531 246.128906 180 270 180 C 293.871094 180 316.761719 170.519531 333.640625 153.640625 C 350.519531 136.761719 360 113.871094 360 90 Z ' />
 </g>
 <g id='rect'>
  <path style='fill-rule:nonzero;fill:rgb(0%,0%,99.998474%);fill-opacity:1;stroke-width:0.03;stroke-linecap:square;stroke-linejoin:miter;stroke:rgb(0%,0%,99.998474%);stroke-opacity:1;stroke-miterlimit:3.239307;'
      d='M 156 185 L 156 95 L 246 95 L 246 185 Z '
      transform='matrix(1,0,0,1,-66,-5)' />
  <path style='fill-rule:nonzero;fill:rgb(0%,0%,99.998474%);fill-opacity:1;stroke-width:0.03;stroke-linecap:square;stroke-linejoin:miter;stroke:rgb(0%,0%,99.998474%);stroke-opacity:1;stroke-miterlimit:3.239307;'
      d='M 66 95 L 66 185 L 156 185 Z '
      transform='matrix(1,0,0,1,-66,-5)' />
 </g>
</svg>

As you can see, here every "g" is tagged with the id defined by the tooltip. This the result viewed in AdobeIllustrator:

enter image description here

share|improve this answer
    
I am using Mathematica v10.0 under Mac, your codes is not working, but the process looks promising. –  Kattern Jun 6 at 4:24
    
I had a previous error in my code that I forgot to update. Now I have changed that line and it should work exp = ReplaceAll[wrapper, XMLElement["g", ___] -> Sequence@@xmlElements];. do you get the same output that I get for the very first part? In your initial question the output is different than mine but we are running the same mma version –  elbOlita Jun 6 at 5:16
    
Yes, the modified version is working! Great! –  Kattern Jun 6 at 5:18
1  
I think the code is a bit rely on the order of group elements in the wrapper is the same as in graphicOptions. Otherwise, the group will get an error id. So I am asking whether there is any reference to make sure the order is right. –  Kattern Jun 6 at 5:43
1  
The graphic's object will for sure be assigned to the correct id as long as it is inside tooltip. There is no change of mixing up since we use this function to assign ids to objects individualObjectsAndTags=Cases[gra,Tooltip[object_,id_]->{object,id},\[Infinity‌​]] Is this your concern? –  elbOlita Jun 6 at 6:02

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.