Exporting graphics to PDF - huge file

109

92

I want to draw some basic surfaces, export them to PDF and include them in a LaTeX file. I create a simple 3D graphics object, for instance with

 ParametricPlot3D[{r Cos[θ], r Sin[θ], r^2}, {r, 0, 1}, {θ, 0, 2 π}]

surface

then context-click on the graphics and select Save Graphic As ..., and save it as PDF.

The resulting PDF is 5MB large! When I include it in my LaTeX file it takes a long time to compile and to render in a PDF viewer.

I know that I can save it in another format such as PNG to get the file size down (in this case all the way to 33KB). But I'd prefer a vector format because my final product will be PDF. Are there some options to the Export[] command which might reduce the number of colors and make the exported file smaller?

Matthew Leingang

Posted 2012-02-09T13:48:34.873

Reputation: 1 325

Adding "ChartBaseStyle -> Opacity[0.99]" to your code could significantly reduce the pdf output file size, but of course Heike's solution is elegant compared to this trick. – Rol – 2016-02-18T09:30:27.240

270 kB Mathematica 11 Windows 7 – Ernst Stelzer – 2016-08-31T08:30:37.933

@ErnstStelzer. Thanks for the data point! But I have neither of those things. – Matthew Leingang – 2016-08-31T17:04:41.080

6This is one downfall of the new (since V6) graphics engine. The exported vector graphics (EPS or PDF) were very useful up to version 5. Would be very interested in a fix, too. Had to change a lot of my export stuff to sufficiently large rasterized images (for which the pdf export works pretty well, esp. with PDFLaTeX). – Yves Klett – 2012-02-09T14:03:17.600

One way is to rasterize the surface but not the axes. See here.

– Alexey Popkov – 2012-02-09T14:21:29.957

Also see here.

– Eli Lansey – 2012-02-09T14:38:41.613

2Are Wolfram aware of the problems with vector export? ...does anyone know ...@Brett?? – Mike Honeychurch – 2012-02-09T22:24:15.320

Answers

88

My preferred method to export graphics to pdf is to do something like

Export["figure.pdf", plot, "AllowRasterization" -> True, 
    ImageSize -> 360, ImageResolution -> 600]

This uses vectors for simple graphics, but produces a high resolution rasterized image if the plot becomes too complicated.

Heike

Posted 2012-02-09T13:48:34.873

Reputation: 34 748

1the documentation for AllowRasterization says "whether to rasterize a graphic that requires advanced versions of PDF". Do you know what is meant by advanced versions? Is that more recent versions than when Mma was written? I've had import problems with recent PDF formats and had to re-save them as "older" PDFs to get them to import. – Mike Honeychurch – 2012-02-09T22:08:02.800

I hink this has the best chance of being close to foolproof, so I'm upvoting it. – Jens – 2012-02-10T03:10:44.960

Making use of this now, thanks. – rcollyer – 2012-11-30T15:49:04.637

3Very nice, upvoting. This also works on a whole notebook. So if bignb has head NotebookObject one can just do, e.g.: Export["tentimessmallerfile.pdf", bignb, "AllowRasterization" -> True, ImageResolution -> 300] – Rolf Mertig – 2012-02-16T09:28:21.577

If I want to export graphics to .eps instead of .pdf what should be changed to the Export[]; it's not working with .eps files, the size remains the same. – Vaggelis_Z – 2013-08-11T12:52:58.387

57

Before you step into the same traps I once stepped, let me point out some key-points. First of all two things: 1. although I spend some time digging in the subject, my knowledge is far from being complete, keep this in mind. 2. as everyone else around here, I would really like to have a fast, good-looking export of vector graphics too. Unfortunately, we have none of those properties currently. Here are some issues I spend some time investigating:

  1. The exported vector-graphics from Mathematica are more or less projection of the (OpenGL-)polygon-scene which is showed in the frontend. This has several disatvantages, first of all a huge amount of polygons is exported. Not only the visible ones, even when you don't use Opacity all polygons are exported. This does not only result in very big file-sizes, it is not uncommon, that those pdf-files need 10 minutes or longer to render in a pdf-viewer. This is simply impossible to use.

  2. Altough the polygons share the same vertex-points, their edges are not completely opaque when rendered in a pdf-viewer. This might result from the alpha-blending which is done at points a line (or a polygon-border) does not lie completely on a pixel-position (which is almost never). This results in artifacts which let you see the polygon-structure. This problem is often discussed, since it happens in density-plots too. If the background is white and the polygons are infront of this background, this looks then like the image below. The situation gets worse when you have surface behind visible polygons. Then you kind of see the 3d-surface through like it would not be completely opaque. I discussed this issue some time ago with one of the developers of InkScape who (and others) was kind enough to explain what happens. You can find this discussion here enter image description here

  3. One particular thing which really bothers me even if I ignore the first two points are the mesh-lines. In the good old days mesh-lines represented really the underlying sampling. This is no longer the case, but since they are just so cute and everyone likes them, they are added to the 3d model. Unfortunately, this is not done correctly since it leads to serious display-error. Even in the above image the mesh-lines do not look equally thick, but taking a closer look shows the chaos:

enter image description here

Idea for a solution

The main-idea is, that while I see the requirement for included text to be in vector-format, for the surface with its smooth lighting and color-gradients it would be enough to have a high-resolution raster image. So maybe we can extract them, transform the surface and put it back together, but how to do it? Like everywhere in science you can easily use the results of smart people who where kind enough to share the knowledge. So please look at the references.

So what you could do is that you plot your Graphics as usual

size = 800;
g = ParametricPlot3D[{r Cos[θ], r Sin[θ], r^2}, {r, 0, 
    1}, {θ, 0, 2 π}, ImageSize -> size];

The next step is to take the Graphics3D and use only the surface without the axes and the box. Since we are rasterizing this graphics anyway, we could in this step smooth out hard edges and supress aliasing effects. A very simple an nice approach was given by @Szabolcs:

antialias[g_] := 
 ImageResize[
  Rasterize[g, "Image", ImageResolution -> 72*2, Background -> None], 
  Scaled[1/2]]

img = antialias[Show[g, Axes -> False, Boxed -> False]];

enter image description here

After having the surface as image you can use Inset to put it back into the graphics. Please note, that Background->None in the antialiasing function makes the white background transparent and therefore it works so nicely with the axes.

final = Graphics3D[
   Inset[img, ImageScaled[{1/2, 1/2, 0}], {Center, Center}, size], 
   AbsoluteOptions[g], ImageSize -> size];
Export["tmp/gr3d.pdf", final]

enter image description here

What you should not now is, that the axes are vector-graphics while the rest is a raster-graphics;

enter image description here

Open Questions

  • If the ImageSize option is not used, the placement of the surface with Inset works fine. Using a higher ImageSize requires an adjustment of the surface-size when it is inset. It's open to be proofed that this works reliable and that the surface is placed correctly.

  • The pdf-export seems to use jpg-encoding for the raster-image. This look ugly. Maybe whe can prevent it anyhow from doing that during the export?

  • Note how nice it works that the box-lines are sometimes over and sometimes under the surface, always right. Inset seems to place the raster really in 3d. Does this always work?

References

halirutan

Posted 2012-02-09T13:48:34.873

Reputation: 109 574

" It's open to be proved..."

I have an example that shows this placement method doesn't always work: https://raw.github.com/peeterjoot/mathematica/master/phy487/rasterizeAntialiasInset.nb

– Peeter Joot – 2013-12-14T01:41:15.153

@halirutan When I copy the code above and execute it, the final figure I get (the one that in your code follows the Export), I get an axis box that seems to be twice as big as the image (That is, the vertex appears to be at the origin, but the top circle at $z=0.5$). Did you use exactly the code in your answer to get the output you show? (MMA 9, if that matters) – rogerl – 2014-04-07T22:29:52.613

@rogerl I (still) get exactly the same output as in my answer. I have tested it in M9.0.1 under OSX Mavericks. – halirutan – 2014-04-08T09:02:43.620

This works in MMA 10.1 for EPS, but everything gets rasterized in PDF. In MMA 10.4, everything gets rasterized, whether exporting to EPS or PDF. (I'm using this on Linux.) – Russ Lyons – 2016-09-11T01:28:59.040

You actually do not get the same picture as the original poster. It is hard to see since it is a cone, but the top edge is outside the box on your picture but not on the OP's picture. If you try it with a cylinder, as I have, it is quite obvious that it is the case. Try with ParametricPlot3D[{ Cos[\[Theta]], Sin[\[Theta]], r}, {r, 0, 1}, {\[Theta], 0, 2 \[Pi]}. The cause of the problem is that when you write Axes -> False, Boxed -> False, it scales the curve up to fill in the space freed up by removing the axes. Then you rasterize a slightly scaled up model. Is there an exact fix? – Anon21 – 2017-02-20T13:58:55.743

@AlexandreH.Tremblay I guess a fix is possible by correctly specifying the viewpoint and turning off the automatic adjustments. However, there is a deeper problem here that should make the whole approach not generally applicable. We are replacing a 3d object with an 2d image plane. While this seems to be working for many cases, I'm sure I can create counter examples where the image covers axes and labels in a wrong way. My answer was not meant to be a solution. Rather it should serve as an idea how one can tackle the problem. – halirutan – 2017-02-23T01:26:43.987

2

Related: "Overlapped Mesh lines in Graphics3D."

– Alexey Popkov – 2012-02-11T12:16:59.263

@AlexeyPopkov I missed that. Did you, just for curiosity, export your 3d-plot to a pdf? It's 1.1 GB here ;-) – halirutan – 2012-02-11T15:44:07.363

No, I export rasterized version with high resolution. But it is also not so easy as one can expect...

– Alexey Popkov – 2012-02-11T17:46:09.133

19

For 3D graphics, I truly don't think it's worth the effort to attempt exporting as vector graphics. The valiant attempts to keep at least the axes and labels as vector graphics are in my opinion not something an everyday user would consider.

With PDF for 3D graphics, you're fighting two problems: not just the file size but also the slow rendering when your PDF reader has to execute a huge computation whenever the page containing the graphic needs to be displayed. The argument for vector graphics is typically that it creates smaller files because the graphic is essentially a program that runs at the time of rendering. But if that program (the PDF) just stupidly enumerates zillions points or polygons to be drawn one by one, you get the worst of both worlds: inefficient data representation with lots of data.

So I would just say: retreat to bitmaps, as Szabolcs was saying. This isn't necessarily a bad thing. Consider the example plot

a = Show[ParametricPlot3D[{16 Sin[t/3], 15 Cos[t] + 7 Sin[2 t], 
    8 Cos[3 t]}, {t, 0, 8 \[Pi]}, PlotStyle -> Tube[.2], 
   AxesStyle -> Directive[Black, Thickness[.004]]], 
  TextStyle -> {FontFamily -> "Helvetica", FontSize -> 12}, 
  DefaultBoxStyle -> {Gray, Thick}]

This takes up 200 MB when exported straight to PDF. If instead I export this with

Export["wiggle.png", Magnify[a, 4]]

the file size is a reasonable 170 KB. The pixelated axes can of course be discerned if you look closely -- but so can the imperfections in the 3D plot itself that will always be there due to limited number of polygons.

The actual question was how to export to PDF, so I guess I'll answer it this way:

Export["wiggles.pdf", Rasterize[Magnify[a, 4], "Image"]] 

Edit:

Unfortunately, this isn't foolproof because Magnifiy stops magnifying when the size exceeds the width of the notebook window! If the window isn't big enough to accomodate the desired magnification, the relative scaling of fonts and graphics will be messed up.

Edit 2:

As is discussed in this related question, Magnify will work reliably provided that you specify an explicit value for the ImageSize option of your 3D graphics.

Edit 3:

The remaining problem with Magnify is that it doesn't scale up tick marks properly. So I asked myself how to make @Heike's method of rasterization work automatically for Graphics3D without having to think about the resolution and image size every time.

Of course one could write a custom export function, but in some situations it would be convenient if one could modify the standard export behavior for the entire notebook. To do this, one only has to make sure that all Graphics3D automatically contain some part that requires an advanced version of PDF. In particular, this is the case for polygons with vertex colors.

So to achieve rasterization by default, one could initialize the notebook with a statement like this:

Map[SetOptions[#, 
    Prolog -> {{EdgeForm[], Texture[{{{0, 0, 0, 0}}}], 
       Polygon[#, VertexTextureCoordinates -> #] &[{{0, 0}, {1, 
          0}, {1, 1}}]}}] &, {Graphics3D, ContourPlot3D, 
   ListContourPlot3D, ListPlot3D, Plot3D, ListSurfacePlot3D, 
   ListVectorPlot3D, ParametricPlot3D, RegionPlot3D, RevolutionPlot3D,
    SphericalPlot3D, VectorPlot3D}];

This adds an invisible 2D polygon as a Prolog to every Graphics3D that is created in the notebook (edit: I had to explicitly do this for various wrapper functions that create Graphics3D, such as ParametricPlot3D). My rationale is that Prolog isn't likely to be needed for anything else in my 3D plots under normal circumstances. Now when I try the above plot a in a simple export command such as

Export["a.pdf",a]

I get a high-resolution image that's ready for printing.

Jens

Posted 2012-02-09T13:48:34.873

Reputation: 93 191

13

One workaround I've discovered is to export as SVG, open in Inkscape, and save as PDF. The default parameters there (which I have not explored thoroughly) result in a PDF file size of 324KB.

Matthew Leingang

Posted 2012-02-09T13:48:34.873

Reputation: 1 325

It doesnt preserve the figure labels especially if they are complicated! – Seyhmus Güngören – 2015-02-01T14:31:55.377

4That works for relatively benign SVG - I was even thinking I could write a script for that. But then I realized that even for moderately complex 3D files, the intermediate SVG that you'd have to export can be so large that it will crash Inkscape. The example in my answer below gives a 90 MB SVG file, for example. That's very unwieldy to work with. – Jens – 2012-02-09T18:48:55.740

12

I suggest exporting to a raster format with high resolution. The ImageResolution option is very useful for controlling the resolution. I wrote a little tutorial on how to export images for LaTeX in this answer (since I would just repeat the same thing here, I am linking to it instead).

Note: High resolution raster images will take up a lot of space, but they are fast to render, and with a high enough resolution they give the same quality in printed documents as vector images. The size of included figures should not influence TeX compilation time significantly.

Szabolcs

Posted 2012-02-09T13:48:34.873

Reputation: 213 047

Seconded, but especially for fine line graphics containing e.g. a lot of text as well (e.g. technical drawings) working with the vector graphics was a pleasure and the file sizes and compilation speeds in LaTeX were a joy. – Yves Klett – 2012-02-09T14:13:14.763

@Yves I agree with you, I also don't like how 3D graphics export to a vector format. I don't believe all those tiny polygons are necessary because PDF does support "triangle gradients". There's this little OpenGL to PostScript/PDF library that produces much much better results, so it is definitely technically possible to export 3D graphics to a small fast to render good quality PDF.

– Szabolcs – 2012-02-09T14:27:51.323

@Yves ... if you have the time and the motivation :-) ...

– Szabolcs – 2012-02-09T14:28:47.390

Just don´t hold your breath ;-) – Yves Klett – 2012-02-09T14:34:52.383

2... and to be fair, I would not want to go back to Version5Graphics, got used to pretty things like Opacity or Texture far too quickly. – Yves Klett – 2012-02-09T14:37:16.293

4

Another convenient workaround is to export to PDF, reimport the PDF file and export it again. For my files it reduced sizes from 2 MB down to 200 kb.

Regards Patrick

Edit: Here is an example of an 700kb export. If reimported and reexported, the file size is down to 28k. Quality seems the same to me. Special characters however, like ä or ö from German are not converted correctly though. If anybody has an idea on how to deal with this?

Export["C:\Users\Desktop\test.pdf", "abc äüö"];

reimport = Import["C:\Users\Desktop\test.pdf"];

Export["C:\Users\Desktop\test2.pdf", reimport];

Patrick Bernhard

Posted 2012-02-09T13:48:34.873

Reputation: 687

1

That might just be due to the font embedding issue: http://mathematica.stackexchange.com/questions/15929/graphics-exported-from-mathematica-9-are-very-large-because-even-standard-fonts

– Peeter Joot – 2013-12-13T15:40:22.653

1To illustrate your solution, you could add example code and images (is there any quality difference...?). – Yves Klett – 2013-09-06T12:30:21.787