Build Great PDFs on the Web Using SVG
Li He is a senior engineer and technical architect in the Aladdin Wealth Tech group within BlackRock Solutions
Printing and framing photos is usually a pretty straightforward task. You can typically get a product finished to satisfaction in a single day by bringing a USB stick to your nearest hobby & crafts store, where they will print your photo on a high resolution printer, cut, and fit it to a frame. Overall it is a pleasant, painless customer experience.
But, imagine if the process were much harder. What if framed photos could ONLY be created using a very special printing and framing machine, which had to be operated by a highly trained technician on a per-photo basis? This technician would have to set up a meeting with you to talk through the photo, get detailed specifications, and then custom order parts and ink. Additionally, imagine the store only had a few technicians who were already working on several projects so you would have to go on a waiting list to get an appointment. That would be pretty terrible, right?
A version of this interaction is exactly the kind of thing that used to happen behind the scenes between designers and developers when crafting dynamic web content.
In HTML/JS space, a lot of this has been mitigated significantly in recent years thanks to great advances in tech stacks for HTML/JS content. Innovative patterns have allowed developers to easily translate designs and express them as a combination of templates and code. As a result, the feedback loop between designing, creating, and testing ideas on real users was heavily shortened, and websites improved quickly as a result.
Users finding text hard to read on your site? Change the font size or color. A particular button is hard to click? Move it around and make it bigger and see what happens. Modern websites today are constantly evolving at breakneck pace to meet the needs of the user.
But, not everything has evolved in the same way. Have you ever logged onto a very slick, modern, well-designed website, perhaps to pay your bills, only to click “Download Statement” and get a PDF that appeared to be totally out of date compared to the design of the web UI? The reason is simple: since PDFs are not typically part of the updated web stack, the task of creating them often remained a laborious task of manually updating old scripts using some combination of iText or pyPdf libraries. In an age of digital screens everywhere, people still really want a nice, printable pdf in many cases. So, the relative dearth of good techniques for creating pdf content can be a real problem for developers and the products they work on.
A Tale of Two Workflows
In the Aladdin Wealth team, our core mission involves taking a holistic approach to help financial advisors build better portfolios for their clients. One way in which we do that is by calculating sophisticated analytics on their book of business, and presenting the information in highly customized visuals (which we call Exhibits) for clients which helps to highlight, measure, or explain what’s happening in their investment accounts. To offer a personalized experience, these exhibits are constantly evolving to match the investment needs of the intended audience.
Our team has always been adept at building and adapting these Exhibits quickly in the frontend, but when it came time to package up the same exhibits into an overall PDF report, we quickly found that creating these reports using traditional PDF generation methods at scale was hard. While we had some specialized software available to reduce some of the PDF boilerplate, we needed developers proficient in that software to wrangle the web content into the right proprietary template format, and also manage the special custom plugins around certain features.
Getting back to the old photo framing analogy, when one chooses to frame a photo, the most important questions should be around choices of the photo and frame itself. When the barriers to solving these problems are low, we can iterate on these problems through trial and error: change the photo, change the frame, see how it looks, repeat. However, if the process of printing a photo itself is arduous, then valuable time and effort will instead be diverted to debating the costs and benefits of changing anything at all.
Surveying many peers across BlackRock, we found that many other teams were struggling to answer the same question: How could we build printable client PDF reports in a way that could keep up with the rapid development and change of our frontend UIs?
To help answer this question, one year ago, we took a step back and examined our workflow. What we realized was that our PDF process was completely separated from our design process.
Client teams worked with clients to propose an exhibit to display some key piece of information. From this point the workflow was handed off to the frontend developers and designers to work out the business logic and data binding details and create an ideal visual for the client. Once a few variations of this was approved by the client, the design and data for the visual would then be written as a frontend component. Finally, all the details of the component would be frozen into an exact spec, parameterized, and passed onto a separate team of PDF-savvy developers to duplicate.
There were two major problems to this approach:
First, the PDF itself was much more time consuming to write code for than the frontend components they were mimicking. The developers had to wear all hats as data wranglers, surrogate designers, and pixel pushers because they could not reuse any of the web code or assets already created. Pixel pushing was particularly difficult because they had to generate real PDFs and open them in PDF readers before any sort of visual confirmation could be obtained. Worse, the engineers were altering designs for purely technical reasons because they were hard to implement in the PDF code.
Second, while PDF developers were consumed with the development process, the visuals could not be altered further by the designers or frontend developers, because any tweak to the design on the frontend could translate to inconsistencies or breaking changes on the backend.
Finally, since all designs started with the browser in mind, certain limitations in print were an “afterthought”. Very often, designs did not include considerations for paper size, lack of browser events, or pagination and were left to the PDF developers to figure out for themselves.
Unifying Web and Print Through SVG
Even as we had plenty of capacity from frontend developers, the bottleneck remained at PDF printing due to reasons stated above. If only we could bring PDF Print into the domain of dedicated frontend developers…
As it turns out, when framing printing as a frontend problem, all the questions we had were resolved with a kind of clarity. Imagine a web developer being asked to develop a view that was:
- Built from 100% known inputs and outputs. Unlike a dynamic web page where content can change at any moment, printing typically only happens after a user has finalized all their inputs and expect a certain report to be produced.
- Completely static. On physical paper, one need not worry about events such as the user clicking on some button that re-sorts your data or changes the DOM in some way.
- Absolutely no responsive requirements! Support is only needed for a few known paper sizes with exact dimensions, and there is no concern for how the user might resize the window or tilt their device.
Frontend developers have spent a lot of time thinking about responsive design for many years already, and deal with interactive content and multiple screen resolutions all day. Already, we have established conventions for supporting alternate views for landscape/portrait orientation, so building an alternative static view for exactly one resolution seemed an almost trivial task.
Even problems like pagination and text wrapping weren’t hard once we thought about it like “Working inside a div with explicit width and height.”
The more we thought about this, the more the idea made sense. If our developers could simply build printable pages as if they were web components, the entire print process would become just another Component task that could be taken on by any frontend developer and leverage the full benefits of hot-reloading, instant preview, type checking against frontend stores, unit testing, and so on.
We simply needed to find out how to accomplish this in a way that the end result of the component could be “rasterized” into a PDF format at the very end. We needed some kind of technology that we could easily drop into our existing stack that could seamlessly transform into PDF with WYSIWYG-like ease, but keep all the declarative power that a developer has at their fingertips when writing raw HTML or CSS. But, does such a thing exist?
This is when we turned to good ‘ol SVG. Essentially a vector image format, SVG translates smoothly into PDF on many platforms thanks to a long and strong history of development in the OSS community. We spun up a lightweight route on our server that simply took rendered SVG and spit out a pixel perfect PDF file. At the same time, SVG was also exceedingly easy to work with on the frontend, since it was just simple markup. Following the documentation out there, we were able to create several Exhibits in raw SVG in a matter of hours.
By defining the proper props, we transformed our exhibits to emit SVG images like below.
We also leveraged libraries such as Highcharts and D3 to produce far more complex visuals. Since those libraries work primarily in SVG, they fit in naturally into the pattern as well.
Putting It All Together
Suddenly, these visuals now also had the benefit of being scalable vectorized snippets that were exportable to PDF (and PNG if we so chose), which led to another logical conclusion, and the leading example of this blog post: framing art pieces for a gallery. We began thinking of the Exhibits as exactly that: pure images that could be pasted regardless of whether the frame is an interactive web application or a piece of paper.
To that end, we established a set of rules for writing SVG Exhibits:
- An SVG Exhibit component has exactly one role: take in props and produce SVG code in the render step accordingly. All other business or layout logic must be handled further up in the component tree
- SVG Exhibits will not responsively alter its own shape. An image’s role is simply to produce itself on the screen to spec. It is not aware of the size of frame it is being put in. It is the responsibility of the parent component to handle aspects like scaling, cropping, etc and simply apply those to the SVG component being produced, just a print shop will resize or crop a photo to match the size of the physical frame.
- SVG Exhibits are written as pure stateless components. The output of the SVG must be predictable given the inputs.
- SVG Exhibits are written to be portable. In other words, they followed a similar philosophy as Styled Components, where each component is explicitly styled, dependencies are explicitly imported, and any CSS rules are inlined. This ensures the SVG produces identical results regardless of being rendered in our home application, an external embed, or on the server.
Using these ground rules, Exhibits became SVG snippets that could then be copy/pasted almost like scrapbook images, which we could then kick back to the application to control, arrange, and resize at will.
This significantly improved our team’s throughput, as responsibilities could be split cleanly as to who was developing the exhibit vs. who was composing the overall report. Further, even the Report itself was designed in SVG. New client reports were simply mocked up as entire SVG pages, with exhibits living as nested SVGs inside.
Through this, we also realized an unexpected benefit when one of our client teams reported back with some suggested changes after a round of feedback:
Our client could also use SVG.
A typical workflow at a lot of web shops is working with the client to come up with some kind of mockup wireframe. These then get sent through to a developer and translated into HTML and CSS.
But once our Exhibits started dealing in SVG directly, the mockup itself became the base of the component. With tools like Sketch and Illustrator able to emit SVG directly, designers simply sent out fully constructed SVGs with a few key elements tagged with IDs. The developers then took these SVGs, performed some cleanup and optimization, and threw them directly into the render methods with appropriate data binding.
The reverse also happened: a developer could supply a designer with the SVG files, and let the designer tweak them directly in Sketch. At the end of the day, both designers and developers were speaking one common language that could be expressed in code.
To Wrap Up
We had fundamentally changed how PDF pages were developed. By allowing each person within our team to work directly in their comfort zone, we were able to gain huge efficiencies and improve the quality of life at all stages, and shorten the time to develop each new exhibit. Creating formerly complex print templates became a simple frontend task no more difficult than adding a new chart or table.
This freed up the developer to worry about things like component life cycle and binding correct data to the component instead of peeping pixels to match up to a mockup. Meanwhile, the client team and graphics designers could work directly with the end client to update SVGs in real time, and all that was needed to communicate these changes to a developer at the end of the day was a simple click of the Export as SVG button and emailing the results.
It’s been a year since embarking on this mission to overhaul and simplify our entire PDF printing process. As of today, not only have we fully retired our legacy PDF generation code and replaced it with our new SVG components, we are also able leverage those same components to generate ‘real-time’ previews of financial reports before they are even printed. This allows our users to toggle certain exhibits on and off, add customized logos and commentary to their reports in real time. Our clients are using the feature thousands of times each quarter.
Chris Coyier, co-founder of CodePen and CSS tricks, once remarked at a talk “SVG is for everybody.” We agree wholeheartedly.