Search code examples
rubypdfsvgimagemagickrmagick

How do I improve quality when using RMagick/ImageMagick to convert from SVG to PDF?


I use Rubyvis to generate SVG plots, and then allow the user to save either to SVG directly, or to some other format using RMagick.

The SVG plots have a set size, which is specified in the SVG file. It seems to me, then, that it should be trivial to convert to a PDF of the same size.

Unfortunately, this appears not to be the case. I can produce a PDF in this manner, but it is much larger (dimension-wise) than the PDFs produced if I first open the SVG in inkscape and then print-to-file as a PDF.

Worse, the PDF image quality is terrible.

Am I missing some instruction for Magick? Here's the code:

image = Magick::Image::from_blob(svg_string_data) { self.format = 'SVG' }
image[0].format = 'PDF'
image[0].to_blob

I then write the value returned (the PDF blob) directly into a file.


Solution

  • The answer comes to you based on a tip from cptjazz on github.

    First of all, the documentation for RMagick is often wrong. I doubt this is a version problem, because the ImageMagick-hosted docs are v2.12.0, and I'm using v2.13.1. Either way, here is what you need to know.

    The docs claim you can use image[0]['pdf', 'use-cropbox'] = true, since true.to_s yields the String 'true'. In fact, it needs an explicit string, and the []= method takes only one key, not two.

    I did not experiment with pdf:use-trimbox, mainly because I wanted an option that also works for postscript. For postscript, you should be able to amend it only slightly, and set ps:use-cropbox to 'true', but RMagick's docs are unclear as to how one may properly set the geometry on a PS, PS2, or PS3. (Normally in ImageMagick, one would set the density to '300x300' and the geometry to '24%', supposedly.) And for some reason, PS3-format output files do not scale as well as the PDFs produced by RMagick. But I digress.

    Here is what I used for PDF:

    image = Magick::Image::from_blob(svg_string) { self.format = 'SVG' }
    image[0].format = 'PDF'
    image[0]["use-cropbox"] = 'true'
    image[0].to_blob
    

    And here is what I used for PS:

    image = Magick::Image::from_blob(svg_string) { self.format = 'SVG' }
    page  = image[0].page.dup
    image[0]["use-cropbox"] = 'true'
    image[0].format = 'PS3'
    image[0].density = '100x100'
    image[0].page = page
    image[0].to_blob
    

    For some reason, setting a higher density makes the image smaller. Why that should be is a mystery.