# Why do grid marks appear when exporting SVG but not EPS?

Observe the following Mathematica Code:

nd[y_, a_, b_, n_] := (Binomial[b - a + 1, y]*Sum[((-1)^i)*Binomial[y, i]*((y - i)/(b - a + 1))^n, {i, 0, y}]);
Unprotect[ColorData];
ColorData["My_Rainbow"] = Function[x, Blend[{Black, Purple, Blue, Cyan, Green, Yellow, Orange, Red}, x]];
Protect[ColorData]
Manipulate[
ArrayPlot[Table[nd[y, 1, K, n], {K, 1, 50}, {n, 1, 50}],
ColorFunction -> Function[{y}, ColorData["My_Rainbow"][y]],
ColorFunctionScaling -> False, DataReversed -> True, Frame -> True,
FrameTicks -> Automatic,
FrameLabel -> {Rotate["K", -90 Degree], "N"}], {y, 1, 50, 1}]


I've noticed some very unusual results attempting to export the graphics as both an SVG and EPS vector image. When I export it as EPS, it behaves exactly as anticipated; it looks just like it does in Mathematica, and nothing gets rasterized, which is fine. But I'd like an SVG image so I can display it on the web. However, when I export as SVG, I get some really strange grid lines that appear! Take a look below:

As you can see, there are some very noticeable grid lines that appear in the SVG version, which change inconsistently with the zoom level, making them rather annoying. Obviously, I'd prefer to not have the grid lines, so the reasonable solution seems to be to convert the EPS to an SVG, right? Unfortunately, whenever I do that, I get this blurry mess!

It's very frustrating, because no matter what I try I can't get what I want. I just don't understand what's going on. If somebody could please shed some light on the situation, I would greatly appreciate it. Thank you very much.

This problem is known and I had a lengthy discussion with one of the developers of Inkscape about this some years ago. Basically, it is a problem of the renderer (aka your browser) and is connected to alpha-blending that happens when you have polygons that share an edge. Additionally, this problem was described in several other posts. See here and here for instance.

The solution is to give the polygons a non-zero edge with the same color. In the case of Raster which is the underlying primitive of your graphics, this is not easily possible. The only easy way I see currently is to turn your Raster into a set of Rectangle primitives where you can set the EdgeForm. That being said consider the following small function:

rasterToRectangle[Raster[grid_, {{xmin_, ymin_}, {xmax_, ymax_}}, _]] :=
Table[
With[{col = RGBColor @@ grid[[y + 1, x + 1]]},
{EdgeForm[col], col, Rectangle[{x, y}, {x, y} + 1]}
], {y, ymin, ymax - 1}, {x, xmin, xmax - 1}]


This can be used to replace the Raster inside your graphics:

nd[y_, a_, b_,
n_] := (Binomial[b - a + 1, y]*
Sum[((-1)^i)*Binomial[y, i]*((y - i)/(b - a + 1))^n, {i, 0, y}]);
Unprotect[ColorData];
ColorData["My_Rainbow"] =
Function[x,
Blend[{Black, Purple, Blue, Cyan, Green, Yellow, Orange, Red},
x]];
Protect[ColorData]

gr = ArrayPlot[Table[nd[13, 1, K, n], {K, 1, 50}, {n, 1, 50}],
ColorFunction -> Function[{y}, ColorData["My_Rainbow"][y]],
ColorFunctionScaling -> False, DataReversed -> True, Frame -> True,
FrameTicks -> Automatic,
FrameLabel -> {Rotate["K", -90 Degree], "N"}]

gr /. Graphics[raster_, rest___] :>
Graphics[{rasterToRectangle[raster]}, rest]


This will look good in your browser, however, it will increase the file size drastically! You should consider using a simple (and extremely small) png file for your web stuff.

• (+1) This doesn't remove the mesh completely (at least on Windows 7 x64): here is a screenshot from Firefox 57.0 (very little visible, but still...). – Alexey Popkov Nov 21 '17 at 13:55
• @AlexeyPopkov It will always be a hack around the original problem. I'm not even sure my solution is scaling-invariant. It's possible you will see lines again when increasing the graphics. You can try to increase the thickness in EdgeForm to close remaining gaps. – halirutan Nov 21 '17 at 15:09

Same problem for me (mma v11.2.0, OSX). Here is a workaround which seems to work for me:

ExportString[YOURARRAY, "SVG"] //
StringReplace[#, {"stroke-width:0.03" -> "stroke-width:0."}] & //
Export["reparray.svg", #, "Text"] &


After investigating a little bit, it seems that mma produces by default an extra mesh, different from the one you would get by adding the options Mesh->True, MeshStyle->Blue for example to the ArrayPlot function. Setting these options to Mesh->False, MeshStyle->Opacity[0] do not change the problem also.

Edit:

The results seem to differ from one browser to the other and if you take a closer look (as remarked in the comments), you still see some mesh, it is not perfect.

Unexpectedly, if I replace "stroke-width:0." with "stroke-width:0.49" it gets better. But, if you play with this parameter you'll see it is hard to predict the result ... Also, if you zoom in (in your browser) you'll see some changes (for 200% zoom, I get a perfect result).

Here is a comparison in different browsers (Chrome seems to give the best result for stroke width 0., and Safari for width 0.49.):

• (+1) This almost works (MMa 11.2.0, Win7 x64): the mesh very little but still is visible: screenshot from Firefox 57.0. – Alexey Popkov Nov 21 '17 at 13:49
• @AlexeyPopkov I didn' t check with Firefox, in Safari it is better but yes it is not perfect also.There is no clear relation between the stroke width and the result ... For larger values the background grows. Strange. See my edit. – SquareOne Nov 22 '17 at 18:44
• @SquareOne Let's tackle this problem from the other side. I asked on SO what the correct solution from the point of SVG would be. – halirutan Nov 23 '17 at 9:13
• @halirutan Well done ! – SquareOne Nov 23 '17 at 14:14

Here is another hack that I learned from this answer (this means, go and vote for it!). We can use a different rendering for different parts of SVG. To include this in into the SVG created by Mathematica, we need to improvise and this should probably be done on the XML structure and not by replacing strings. For my answer, I won't go through this.

What we do is that we use exact rendering for the glyphs (aka your fonts) and crisp rendering for the surface. This will work in your case. When gr is your graphics, then you can do

str = StringReplace[ExportString[gr, "SVG"],
{vb : "id=\"surface" :> "shape-rendering=\"crispEdges\" " ~~ vb,
vb : "<symbol " :> vb ~~ "shape-rendering=\"geometricPrecision\" "}
];
Export["reparray.svg", str, "Text"]


and you will end up with an SVG that looks like this

• +1 for you and for the linked answer! Very nice, I do not see any mesh even after playing with resolution in Firefox 57.0 on Win7 x64. – Alexey Popkov Nov 23 '17 at 17:13
• @AlexeyPopkov There is one drawback though: In case we ever want to plot an irregular shape, the outer boundaries will not be anti-aliased. – halirutan Nov 23 '17 at 17:28
• Despite this limitation it is worth reporting to WRI as a suggestion for improvement for the case of Exporting Raster to SVG, I think (I'll report if you don't mind). – Alexey Popkov Nov 24 '17 at 2:17
• @AlexeyPopkov I don't see much hope for that, but sure if you like, you can report this. For me, it would be much more important to get all graphics primitives for a Graphics or Graphics3D. This includes all axises, ticks, etc.. With something like this in hand, one could finally write better and more specialized exports. But this is another thing I don't have much hope for. – halirutan Nov 24 '17 at 3:03