Skip to content

Rendering heavy SVG files (improving performance)

In our company we want to come up with universal solution for creating interactive presentations as mobile applications. First idea was to create a PDF file and use it in mobile phone, it didn’t work out – it was too slow. Another idea was to convert PDF into SVGs and use them as scenes (slides) and that’s what I am working on right now. What I forgot to mention, that PDF contains internal link annotations to navigate between pages.

So, for PDF to SVG conversion I use pdf2svg cli tool. I also wrote PHP cli application to parse all the links from PDF with their position. For conception I use ReactJS to test this idea on the WEB first (I have never worked with React Native before).

Now the problem: PDF contains a lot of high resolution images and a lot of pages, so some of SVG files are very large (up to 11MB) and size of all SVGs is ~70MB. When rendering these large SVG files, there is a delay (~1-10 seconds), comparing to PDF file that’s not a huge win, so I need to optimise loading time.

What I have tried so far:

  1. With earlier mentioned PHP CLI utility I wrote, I put some data about links inside SVG files (<rect x="..." y="..." width="..." height="..." data-target-page="..." opacity="0"/>). Then I rendered SVG by containing page number inside state with <object data="..."/> and on each render created onClick event listeners for <rect> tags inside SVG for navigation. Well, it was first try and I wasn’t satisfied with performance.

  2. I tried to use react-svg-loader to inject SVGs as components. It didn’t work out, performance was even worse (well, converting 70MB SVGs to JSX components doesn’t sound good). By the way, I tried to build project for production, it tooks so long I just couldn’t wait. So, not an option.

  3. Instead of SVG, I tried to use PNG images with smaller resolution (each PNG was about 800kb) and put links as div elements on top of an image, performance was really good, but I lost quality. So not an option.

  4. Same as 3, but with SVG and <img src="..."/>. I think it is slightly better, but still not a win.

Do you have any suggestions how could I improve performance by still using SVG? Should it perform better or worse in React Native?

Answer

I think the main part of your problem is this: pdf2svg embeds all raster images as base64-encoded ASCII strings inside the SVG. Converting and rendering these seems to take significantly more time than loading and rendering an image that is referenced and stored in external PNG or JPEG files.

Unfortunately I do not know a CLI tool that can, on importing a PDF, split out the embeded raster images into extra files. But the GUI SVG editor Inkscape can: Open a PDF file with Inkscape, and a dialog pops up that asks you not only about what page to select, but also showing an option “Embed all images”. If you deselect this checkbox, the images will be stored as separate files in the directory the PDF is loaded from and only referenced in the form

<image xlink:href="image0.png" ... />

My suggestion would be to immediately save the imported page as “Plain SVG” and then adapt the folders and paths with other tools, as the structure of the produced SVG is a bit convoluted. It is easier to find the <image> tags with a search-and-replace routine than to rummage for them inside Inkscape.

Another method might be to initially let the images be embeded. They all have the form

<image xlink:href="data:[<mediatype>][;base64],<data>" ... />

You could extract them from the SVG with some search tool and then decode them with uudecode into image files, and then replace the data strings with references.