Re: Using Magick++ to convert large PDFs into individual PNGs
Bob Friesenhahn <[email protected]> Tue, 9 Feb 2021 08:54:11 -0600 (CST)
| Newsgroups | gmane.comp.video.graphicsmagick.apis |
|---|---|
| Message-ID | <[email protected]> |
On Tue, 9 Feb 2021, Lukas Studer wrote: > > First of all, I'm new to GraphicsMagick and the management thereof, so I > am not quite sure if this is the correct mailing list. > > I'm am using the Magick++ API to read large PDF files (ballpark of about > 2000 to 3000 pages, size of 100MB and upwards) and store each page in a > database (not shown in snippet). While doing so I run into resource > issues, namely not enough RAM, when reading the PDF file resulting in > basically, or what feels like, an infinite runtime. You are facing a long-term design issue regarding the interfacing of GraphicsMagick and Ghostscript as well as how GraphicsMagick works in general. GraphicsMagick asks Ghostscript to convert all (or a range) of pages to an intermediate file format (e.g. a PNM sub-format) and then reads all of the pages before it writes any output. This approach does not scale since the system will run out of resources. > My question now boils down to, what is the proper API for such a use > case, i.e. how does one handle such big PDF files using the Magick++ > API? Is there a way to do this in batches or in a streamed fashion, > where I read page by page and directly store them? It is possible to succeed by iteratively setting the 'subimage' and 'subrange' properties so that only a few pages are included in the conversion at a time. It turns out that Ghostscript is pretty efficient at finding the first specified page in a PDF sequence. > I know of the syntax involving the indexOf operator to only read a range > of pages, e.g. "myFile.pdf[0-100]". I was hoping to solve the issue > directly via the Magick++ API. Given the advice to set the 'subimage' and 'subrange' properties you would be able to succeed, but then the next problem is hit in that I see this FIXME: statement at the top of the readImages() implementation in Magick++/STL.h: "FIXME: need a way to specify options like size, depth, and density." The FIXME: statement should also have mentioned 'subimage' and 'subrange'. There is yet hope given that the readImages() template function is in a header file and is very short so a modified implementation can be added. The next trick is that the Magick::Options implementation is described as "This is an internal implementation class and is not part of the Magick++ API" so it can't be used. There are also subImageImage() and subRangeImage() STL function objects, but they are not intended for this purpose. Ultimately, what is needed is to update 'subimage' and 'subrange' in this ImageInfo instance similar to how filename is updated. MagickLib::ImageInfo *imageInfo = MagickLib::CloneImageInfo(0); So, what can be done is to duplicate the existing readImages() template function and create a new one with two more integer arguments for 'subimage' and 'subrange'. Then assign those arguments into the ImageInfo struct. This would allow you to request a reasonable number of pages at a time. The last tricky thing is to detect when the end of the document has been reached. If just one page at a time is requested, then this is not so hard, but it is less efficient than several pages at a time. Perhaps when an error occurs, then switch to one page at a time. A more sophisticated readImages() could add a loop so that the implementation does the per-page iteration itself. Lastly, there is a completely different solution, which is to ignore the existing STL implementation. Instead, use the Image class and its read method which is only willing to read one image at a time from the sequence. The Image class provides subImage() and subRange() so you should be able to read one page at a time. I apologize for such a lame implementation. Bob -- Bob Friesenhahn [email protected], http://www.simplesystems.org/users/bfriesen/ GraphicsMagick Maintainer, http://www.GraphicsMagick.org/ Public Key, http://www.simplesystems.org/users/bfriesen/public-key.txt