## ImageCrop for vector graphics

18

11

I have a plot with some whitespace I'd like to get rid of. Ideally, I'd like to use a command like ImageCrop, but for non-raster graphics (ImageCrop rasterizes the graphic). The Mathematica documentation tells you how to do this by hand (under the "cropping" section):

http://reference.wolfram.com/mathematica/tutorial/InteractiveGraphicsLayout.html

...but there doesn't seem to be a way to do this via a command. I've tried all sorts of combinations of FrameMargins, ImageMargins, ImagePadding, etc, but to no avail. Even if I could just manually specify the number of pixels to crop by, that would be an improvement over the current situation, but ideally the best solution would be to replicate ImageCrop for non-raster graphics.

Have you looked into the option setting Method -> {"ShrinkWrap" -> True}? – J. M.'s ennui – 2012-05-15T04:46:18.147

Gave that a try, it seems to have bizarre behavior, it crops out the tick labels entirely. It might only work for raw Graphics objects? I'm trying to do this with a Plot object. – Guillochon – 2012-05-15T04:57:57.150

3Could you please give code for your plot and indicate which white spaces? – Vitaliy Kaurov – 2012-05-15T05:20:26.750

The big question here: do you want to do this with the 'contents' of the graphics, i.e. all the graphics primitives? Or do you want this to work for all labels, tick marks, frames, etc.? If you only need the former, Show[g, PlotRange -> All, PlotRangePadding -> 0] should do it. If you need the latter, then you just opened Pandora's box ... – Szabolcs – 2012-05-15T07:47:32.353

If you need the latter, you can start with dirty hacks like this: http://mathematica.stackexchange.com/questions/2091/retrieving-the-imagepadding-in-absolute-units (I uesd this several times, but I don't like it at all.)

– Szabolcs – 2012-05-15T07:48:35.410

In my answer, I started out trying to emulate what I believe the interactive editing tools are doing. Interestingly, the interactive editor doesn't work with Overlay objects, whereas my answer does. – Jens – 2012-05-15T16:04:05.527

@Szabolcs: Indeed, I am interested in opening the "Pandora's box", as I am just trying to crop the whitespace around a plot object, but still include all the tick labels, etc. Jens' solution works nicely. Sorry to not have provided a more specific example, my full code is a mess. – Guillochon – 2012-05-15T17:47:11.427

18

Edit

Come to think of it, one case where I often want to crop the output is Graphics3D (one can do it with ViewAngle etc., but it's not always convenient - one could also convert to bitmap, but again that's not always desirable). So I decided to allow the cropGraphics function below to be used with any object, not just 2D Graphics. Here is an example:

q = Show[ExampleData[{"Geometry3D", "Cow"}], ImageSize -> 360,
Background -> Darker[Green]]


cg = cropGraphics[q, -0.8, -0.4, 200, 200]


In the simple form defined below, cropGraphics requires the input object to have an explicit ImageSize setting. But it works for a wide range of objects. It can even be used to convert Overlay objects to Graphics while at the same time cropping them.

Speaking of Overlay, here is a continuation of the above example:

plot = Overlay[{Plot[Sin[x], {x, 0, 2 Pi}, GridLines -> Automatic,
Frame -> True, ImageSize -> 360],
Magnify[cg, .5]}, Alignment -> {.5, .5}]


This is an overlay with the cropped graphics created previously. Now crop this plot once again:

cropGraphics[plot, 0.5, 0, 300, 120]


End Edit

Here is the function that does the above. It is based on Inset, and it assumes for simplicity that the graphic you want to crop is going to be positioned in the crop window by placing the original coordinate {0,0} (in the coordinate system of the graphics) at a scaled position {x,y}. Here, x and y are between 0 and 1, with x = 0.5 corresponding to the center of the crop window. I'll explain this in an example:

cropGraphics[g_, x_, y_, w_, h_] :=
Graphics[Inset[g, {x, y}, {0, 0}], PlotRange -> {{0, 1}, {0, 1}},
ImageSize -> {w, h}, AspectRatio -> Full]


Here is a sample graphic; I would suggest always giving it a definite setting of ImageSize to begin with:

pl = Graphics[{{Red, Rectangle[{0, 0}, {.5, .5}]}, {Blue,
Disk[{1, 1}, .5]}}, PlotRange -> {{0, 1.5}, {0, 1.5}},
ImageSize -> 400]


For reference, the bottom left corner of the square is at {0, 0}, and the graphics is 400 pixels wide and high.

Now we crop it:

cropGraphics[pl, 0, 0, 300, 300]


The coordinates 0, 0 mean that the bottom left corner of the square is still where it was before, but the new image width and height are 300.

If we want to crop part of the square instead, all we have to do is move its lower left corner {0, 0} outside the crop window by giving it negative scaled coordinates:

cropGraphics[pl, -.25, -.1, 300, 300]


Finally, an example of a non-square crop area:

cropGraphics[pl, 0.5, -.24, 400, 300]


One could improve on this by changing the scaled coordinates x and y to something absolute in pixels, but I wanted to keep it simple for now, to focus on the main issue: use Inset with a variable position in a "wrapper" graphic whose coordinate system ranges from 0 to 1 to make the inset position a scaled coordinate, and whose ImageSize has the value desired for the crop.

– Alexey Popkov – 2013-10-30T18:47:39.880

(+1) It is really usefull; however, when try import the cropped .eps file the size of the exported file become very large which is strange! Also, It seems that the quality has been reduced a little. – Hosein Rahnama – 2017-05-23T09:46:35.497