Jethro Daniel
Posted on November 15, 2020
So, the other day at work, i got a ticket to build a feature to export pdf documents as images. It looked like a straight forward task, but quickly turned out to be a nightmare.
The Problem
Many of the PDF spec implementations in circulation today are always almost natively written (C/C++), and many languages all have a wrapper around them, because why re-invent the wheel when there is already a battle tested library right ?
Well, in my case this wasn't helpful, due to some dependency management/server constraints, i was unable to work with these types of implementations.
Solution 1: Get A Library
Since the native way wasnt feasible, i began heavy googling and research, trying to find 100% managed Go libraries, and yea, i found alot of libraries 100% go, but all of them didnt have the feature i really needed, which was exporting the pdf to image, at this point my brain was already experiencing a stackoverflow 🤯
Solution 2: Find A Managed Library In Another Language
Simply write that part of our system in another language, that had managed libraries to work with, surely enough i got many libraries like that are in C#/Java etc to work with, which is good since i have significant experience in C#. But at this point my paranoia was off the charts, simply accepting a solution like this was not satisfactory to me.
Solution 3: Building My Own Library In A Managed Language
So how hard would it be implementing a pdf to image library in a managed language ? 😏
Well, turns out that it is not so difficult. I started of my journey by understanding what exactly is truly involved, went to github, to look for some ideas, i saw a project with a proof of concept, and basically all they did was to take the PDF document, parse it and extract the pdf objects (images/text) and draw them onto a canvas and save the canvas as an image.
This should be fun i thought, i started writing my own implementation, and in under a day working with C#/.Net Core and its beautiful low level APIs, and this beautiful library ImageSharp i was able to hack something together.
Soon enough all the pdf hidden Spec features began to hunt me, Pdfs with images that had opacity were a nightmare, pdfs with custom fonts also came along and on and on, any new pdf i tried, came with its own twist.
Final Solution
I soon realised that this is another whole project on its own, and since time was running out i decided to fallback to Solution 2 and quickly close the ticket.
Then another roadblock, the perfect library i found in Solution 2 was exporting the image in ARGB32 pixel format, essentially when i try to parse the image i get images in this all blue format, like a snapchat filter.
Thanks to the knowledge learnt in Solution 3, i was able to manipulate the raw bytes of the image and fix the exported image by manipulating the individual pixel. Rearranging them back to RGBA32 pixel format.
Span<byte> imgBytes = reader.GetImage(new NaiveTransparencyRemover(255, 255, 255));
var width = reader.GetPageWidth();
var height = reader.GetPageHeight();
var newImage = new Image<Rgba32>(width,height);
var rowBytes = width * 4;
for (int i = 0; i < imgBytes.Length; i+=rowBytes)
{
var singleRow = imgBytes.Slice(i, rowBytes);
Span<Rgba32> pixelRowSpan = newImage.GetPixelRowSpan(i == 0?0:i/rowBytes);
for (int j = 0; j < rowBytes; j+=4)
{
var px = singleRow.Slice(j, 4);
pixelRowSpan[j==0?0:j/4] = new Rgba32(px[2],px[1],px[0],px[3]);
}
}
using (var fs = File.Create(Path.Combine(Environment.CurrentDirectory, $"wave-{DateTime.Now.Ticks}.png")))
{
newImage.SaveAsPng(fs);
}
Summary
This was an interesting ticket to work on, i learnt alot about computer graphics, pixel standards, the PDF specification and all, i hope i can comeback and work on a 100% easy managed solution in Go as a side project.
Posted on November 15, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.