Introducing the View API

Sep. 17 2013

Today at BoxWorks Developer Day, we announced the new version of Crocodoc: the View API. It’s now available in public beta! You can read more on the Box Platform blog.

We’re incredibly excited to bring the technology that powers the new Preview experience on Box to developers via the View API. Documentation for the beta version of the API can be found here. Be sure to register for a Box API Key and start experimenting with the technology today.

Join Box + Crocodoc at BoxWorks Developer Day on September 17

Aug. 30 2013

As we’ve learned over the last three months, one of the perks of joining the Box Platform team is the opportunity to participate in some truly epic initiatives. Today, we’d like to invite you to join us for one - an event designed specifically for the developer community that promises to be truly special. At BoxWorks Developer Day, some of the brightest minds in technology will share their perspective and vision for building amazing products and companies that have scaled to millions of users globally.

On September 17, luminaries like Dustin Moskovitz, co-founder of Facebook and Asana, and Patrick Collison, CEO of Stripe, will share lessons learned from their respective journeys to enterprise success. Danielle Morrill of Mattermark, David Hornik of August Capital and Sam Schillace of Box will host a roundtable discussion about finding the right problem to solve. Eoghan McCabe, CEO of Intercom, will give insights on end-user design. Jamie Sutherland, President of US Operations at Xero, Jim Patterson, CEO of CoTap and Adam Evans, cofounder and CTO of RelateIQ will help you find your definition of their long-term, sustainable business models. Parse, Firebase, Stripe, Twilio, Docusign, Ink and Okta will be partnering with us to share their technology and expertise at the BoxWorks Developer Day on September 17 in San Francisco at the Bill Graham Civic Auditorium.

Attendees will start the day with Malcolm Gladwell’s keynote at the Hilton before being bused to Bill Graham Civic Auditorium for a day filled with conversations and presentations from these amazing leaders. Box’s CEO Aaron Levie and Chris Yeh, head of Box product and platform, will kick things off in our opening keynote.

Developer day is free, so be sure to register as a “developer” for your spot today! The Crocodoc team will be on site all day, and we hope to see you there!

-The Crocodoc Team

PS: Registration gives you access to the after party. Don’t pass up the chance to join us in rocking out to the sweet sounds of Blink-182!

Crocodoc Joins Box to Reimagine Documents in the Cloud

May. 9 2013

Today we’re sharing some big news: Crocodoc is being acquired by Box!

We’re thrilled to be joining forces with Box and want to share some thoughts on why this is great news for our customers, partners, team, and the web at large. The bottom line is that we’re committed to offering our platform API, investing heavily in its continued development as a stand-alone offering, and integrating our technology into Box’s flagship collaboration service.

From the beginning, Crocodoc’s goal has been to modernize the way we interact with documents, creating a seamless experience for sharing and collaborating on any browser or device. We’ve invested countless hours over the last few years to build the most sophisticated HTML5-based document viewing technology in the world — and that focus has paid off.

Since launching our enterprise API offering, we’ve:

  • Partnered with amazing customers including Yammer, LinkedIn, Dropbox, Facebook, and Edmodo
  • Grown at 30% per month since 2011 and served hundreds of millions of document previews in the last year
  • Built a profitable, fast-growing business that generates millions in annual revenue

Despite our growth, we’ve only scratched the surface of what’s possible. We want to do everything we can to provide the best document viewing and collaboration service for the entire web — and through several meetings with Aaron and the Box team over the last couple of years, we realized that Box and Crocodoc truly share the same vision for the future of documents on the web.

While we’ve been focused on pushing the limits of document viewing technology and building a powerful developer platform, Box has been solidifying their position as the top content sharing platform for both users and the enterprise. Today they serve more than 15 million individuals, 150,000 businesses and 92 percent of the Fortune 500. Together we’re in a position to build something amazing.

As a part of Box, Crocodoc’s technology will remain a stand-alone offering, as a part of the Box Platform, as we aggressively extend our API to developers and enterprises everywhere. Not only will we continue to fanatically support our existing customers, but we’ll be launching a new version of Crocodoc soon and will continue to push the limits of what’s possible with documents on the web.

In addition to developing our core API offering, we’ll be integrating Crocodoc directly within the core Box experience itself. We’ll be drawing on the experience we’ve gained over the years to further redefine document storage, collaboration, and editing in ways that have never before been possible.

Knowing how far Crocodoc has come with only seven people (as well as the support of an amazing group of investors and advisors), we couldn’t be more excited to make waves as part of the Box family. We look forward to continuing our journey with you, our customers, our team and the 700+ Boxers that we’re excited to join in the coming weeks.

Note: and just in case you were wondering, no, Aaron isn’t going to let us rebrand our service as Crocobox ;)

Ryan Damico
Co-founder and CEO

Read more at the Box blog.

Process Managers: The Good, the Bad, and the Ugly

Apr. 23 2013

Sometimes processes just die. It’s unavoidable. In most cases, it’ll give your users a chance to see the snarky error page you made late one night or, more likely, the ubiquitous “502 Bad Gateway” page. The art of getting your servers to automatically recover from unexpectedly lost daemons is part of Process Management. It’s just one of the tools on the sysadmin’s utility belt, but it’s a fundamental one.

There are many different process management solutions out there to choose from. In this post, I’ll walk through some of the more popular options and point out the good, the bad, and the ugly of each.

Obligatory disclaimer: I’m not an expert in process management and the following is my personal opinion and impressions of each solution.


At first glance, Upstart seems like it’s only meant to replace /etc/init.d and similar daemonization techniques. (It says so right on the project’s homepage.) Maybe you already know how to write a half-decent init.d script, so why bother with these newfangled .conf files? Because Upstart does a heck of a lot more than replacing simple start/stop/restart daemonization, including keeping daemons alive!

In many cases, a short Upstart script can replace a more verbose init.d script to achieve the same goal of being able to start, stop, restart, and get the status of a daemon process. Of course, the devil is in the details. The majority of a typical init.d script tends to be dedicated to validating the environment, reading configuration, setting environment variables, etc. Upstart supports all that too. The cookbook goes into detail about everything Upstart can do.

Note: While the cookbook is a great resource, it can be overwhelming for a beginner…it certainly was for me.


Here’s a simple configuration to manage a Django-Celery worker process:

It’s straightforward enough that I won’t go through it line by line, but it is important to note that in this example, Upstart will immediately start the daemon again if it unexpectedly goes away, courtesy of the respawn command.

Save it as /etc/init/celery.conf and then run initctl reload-configuration to let Upstart know about it. (The documentation claims changes are automatically discovered in the /etc/init directory, but that has proved unreliable for me.) The daemon will now start when the server boots, stop when it shuts down, and can be manually controlled by the likes of service celery start or stop celery where the service name is whatever comes before the .conf in the filename, in this case “celery”.


Overall, I like Upstart a lot. Its functionality is rich enough to let power-users achieve complex tasks while still facilitating simple daemonization thanks to well-designed defaults. Since all processes managed by Upstart are subprocesses of a master Upstart process, daemons that exit unexpectedly are immediately detected and respawned.

I like that I don’t have to worry about the question “What if Upstart itself dies?” On Ubuntu and several other distributions, Upstart is used to manage most of the core system-level processes like networking, syslog, ssh, tty terminals, etc. This is comforting. If Upstart dies, you’ll have bigger problems on your hands than your web server daemon disappearing.


Unfortunately, Upstart lacks support for custom commands. The ability to send arbitrary signals to a process or execute arbitrary scripts like init.d scripts allows comes in handy. The convenience of custom commands like /etc/init.d/nginx configtest to check my nginx configuration syntax without affecting the running nginx service is useful enough to keep me from migrating my nginx daemonization to Upstart.


Monit is an established player in the process management game. Its sole purpose is to monitor daemon processes, files, directories, filesystems, etc on your server and respond with appropriate actions whenever something is not as it should be.


Here’s a simple configuration to monitor an SSH server daemon process:

What’s going on here? Given the daemon’s PID filepath and how to start/stop the daemon, Monit will check that the process exists every 60 seconds and start it anew if it is not found. What’s more, Monit will also attempt an SSH connection on port 22 and restart the ssh server process if the test fails.

That last bit is the true power of Monit. On top of simply checking that a process exists, it can perform network tests as well as check system resources like CPU usage, memory consumption, number of child processes, and many other things. This can aid greatly in determining if a webserver is correctly serving traffic on port 80. It’s also a great band-aid for a process known to have a memory leak.

After Monit is initially configured and started, it’s controlled via the command line. Executing monit summary gives the state of all the processes it is monitoring. Monitoring for a given process can be temporarily disabled/enabled with the monit unmonitor and monit monitor commands. This can be useful as part of a deploy process for ensuring a daemon is stopped while a source code directory is rsynced or otherwise updated. A handful of other actions are also available.

By the way, a built-in web interface can be enabled to provide much of the same functionality available from the command line in a more user friendly way. Just be sure to properly lock down the web interface from public access, since it exposes so many powerful and potentially dangerous functions.


Monit is a pretty solid and useful tool. It’s portable and easy to compile since it has very few library dependencies. Note that, unlike most of the other solutions described here, Monit does not daemonize processes; it only monitors them. This can be seen as either a positive or negative. On one hand, separation of concerns is a good way to keep things simple. On the other hand, it’s one more system to maintain.

Monit seems to have a good developer community around it with a fairly responsive mailing list. The wiki includes an amazing collection of configurations examples for just about every common service out there.

Monit can also be used to monitor the existence, contents, and other properties of arbitrary files or directories on the server. While this likely has some interesting use cases, I imagine these features have been somewhat superseded with the advent of configuration management tools like Puppet and Chef.


I ran into several gotchas while getting familiar with Monit.

When specifying the start program and stop program directives, don’t make any assumptions about the environment’s PATH variable; use absolute paths for all executables. This led me to more than a few head-banging moments.

Don’t forget about Monit when manually starting or stopping a daemon it is watching. This can lead to a process either inexplicably being resurrected shortly after stopping it or, even worse, a process left unmonitored once it is started again! Either commit yourself to never forgetting that Monit is running (good luck with that) or get in the habit of using the monit stop and monit start commands when manually controlling daemon processes.

Programmatically issuing commands like monit unmonitor and monit monitor in rapid succession will often lead to errors. To avoid this, use groups intelligently so that only one command is ever required at a time. If groups aren’t enough, adding a one second sleep between monit commands is a reasonable solution.


Supervisor is a Python-based process management solution. It’s one of the newer contenders in the space, and shares design principles and goals with Upstart. As such, it takes care of daemonization as well as process monitoring.


Here’s a configuration analogous to the one shown for Upstart:

Configuration is done in the familiar .ini format rather than a custom syntax as is the case for Upstart and Monit. While it is more verbose, it remains fairly readable. Configuring Supervisor itself is a bit more involved than some other solutions. Fortunately, the folks behind Supervisor realized this and include the echo_supervisord_conf command for you to run on the command line to send a default configuration to stdout.

After everything is configured, start Supervisor by executing supervisord. The processes Supervisor manages are automatically started as subprocesses of the main supervisord process. As with Upstart, a managed process will be restarted immediately if it unexpectedly exits. Note that the autorestart configuration option lets you explicitly define what what qualifies as “unexpectedly exiting”.

Supervisor can be controlled through supervisorctl on the command line. Commands may be issued one at a time (e.g. supervisorctl restart celery) or by first starting a Supervisor terminal (by running supervisorctl with no action specified) and then issuing actions directly (e.g. restart celery).


Supervisor is an exciting prospect! Since it written in Python, it’s arguably less intimidating for the average user to go source-code diving to investigate a bug or satisfy curiosity. It also makes it trivial to install on any system that has easy_install or pip.

Again, since all monitored processes are subprocesses of the supervisord process, Supervisor is instantly notified of a process dying and acts to respawn it instantly. Instant is nice compared to the interval that Monit runs its checks on (which tends to be set to 60 seconds by default).


It’s not all roses. If Supervisor itself were to crash (this should be a very rare occurrence), all daemons it controlled would also go away. Not trying to scare anyone away, just something to consider since it’s every sysadmins’ job to be paranoid and consider all the what-ifs they can.

While the .ini configuration syntax is nice and has a lot of options available, I found it odd and constricting sometimes. Loading environment variables from a defaults file seems all but impossible (please correct me if I’m wrong!). I thought perhaps making a .ini version of a defaults file would work, but still no luck due to Supervisor using an older variant of Python’s ConfigParser class. The simplest way around this I could come up with is to wrap the daemon’s start command in a shell script that first loads any extra environment variables. A hack for sure, but not a horrible one.

Daemonizing Supervisor itself is a task. I would recommend Upstart as a good candidate for doing so. ;-)

I’ve yet to investigate how well Supervisor is supported by Puppet or Chef. This is an important point since Supervisor isn’t the native daemonization facility on servers and will therefore require a custom plugin to properly start/stop/restart processes via a content management system. A third party Puppet module exists and has a respectable following, but I have not tried it out just yet.


Circus is another Python-based process manager similar to Supervisor, but with a twist. In addition to managing processes, Circus can also create sockets and direct traffic to/from them. It’s clearly been designed and tailored with webserver stacks in mind and may not be the best choice for general purpose process management. Nonetheless, it is a relatively new project with some innovative concepts, so I’d say it’s worth looking into.

As it stands now, I have major apprehensions about Circus. After reading a bit about it, I was excited to try it out. It seemed like it could become a core part of all my web projects. It’s maintained by a group within Mozilla, and corporate backing of an open source project is usually a great indicator of good support and forward progress.

Unfortunately, I had a bad first experience with it.

Getting Started

While getting my “Hello, World!” of Circus up and running, I ran into several inaccuracies in the documentation.

The deployment page gives a sample Upstart script to daemonize Circus. Great! A quick copy/paste later, Circus was failing to start up. After looking closer, turns out there are two typos. First, a newline is missing between the respawn and exec directives. Second, en-dashes are used rather than double normal dashes before the log-output and pidfile parameters. That was a particularly frustrating and difficult to spot typo!

I also found a quirk on the configuration page. The [env:program] block in the first example configuration is indented. While still correct syntax, it made me second guess myself since indentation typically is not used in .ini files.

I submitted a pull request and an issue to address these issues. They were accepted/addressed promptly and are now reflected on the website, so kudos to the Circus team for that!

These may seem like nitpicks, and on their own they probably are. However, they could suggest a larger issue if the documentation is rarely proofread or sanity-checked.


Part of the case for Circus is that it simplifies and unifies the management of a typical webserver stack. For example, it can outright replace a webserver like Gunicorn or uWSGI and take over the job of spawning web workers. I won’t try to explain anymore, just read their description. This is exciting since it reduces the number of moving parts in a stack by one since the webserver and process manager are one and the same. This is at odds with the notion of separating concerns, but will likely be an elegant and simplified solution for most users.


After finally getting the Circus daemon up and running, I tried issuing commands to it using the circusctl command line tool. The commands available are similar to those for Supervisor’s supervisorctl command. Unfortunately, the commands would frequently timeout or completely fail without any useful explanation as to why. What’s worse, after one command failed, all subsequent commands would fail until fully restarting the circus daemon. A couple times, the commands would hang indefinitely — consuming 100% of the CPU — until I forcefully killed the supervisorctl process.

These issues were enough for me to lose my confidence in Circus being reliable in a production environment, so I curtailed my evaluation of it. Of course, it’s very possible I misconfigured Circus in a fundamental way, causing myself to experience these issues. Either way, the lack of helpful error messages was not particularly beginner-friendly.

Wrapping Up

There are lots of options in the field of Process Management. The ones I’ve described above are only those that I’ve had the opportunity to take for a test spin and, in some cases, deploy in production. There is a lot of discussion and debate about the right way to do this stuff out there, and the appropriate mix of tools will vary a lot depending on your scenario and requirements. If you are new to this, hopefully I’ve given you enough of an (opinionated) survey of the space to get started. If you already have some experience with process management, I would love to hear what you think!

By the way, if you’re looking for a job and this interests you, Crocodoc is hiring. We’re a fast-moving team and frequently get to evaluate new technologies that can improve our product…or just because we want to! I’d love to have you join us.

written by Matt Long, Crocodoc Cofounder and Lead Developer


See the discussion on Hacker News

This is not meant to be an exhaustive list of Process Management systems. There are many other great tools out there that I simply haven’t had the time to evaluate yet. I’ll do a follow up post on another batch of systems. Please let me know if you have any suggestions!

3D-ifying Documents Using CSS Transforms

Mar. 26 2013

We recently launched a preview of Crocodoc’s newest document to HTML converter. If you haven’t checked it out yet, go play with our preview and see how we’re converting the pages of your documents to embeddable SVG and HTML.

What does the new converter mean to those of you building web applications using Crocodoc? Simple: your documents will load faster, look sharper, and be much easier to customize. Our preview page is full of interactive examples designed to help provide inspiration and showcase what is possible with the new Crocodoc: everything from a 3D page demo, showing off the many layers in a document, to a magnified view of an uploaded document, and a thumbnail that expands into a full-size inline document.

For this post, I’d like to focus on the 3D demo. I’ll explain how it was built, the various issues we ran into, and the workarounds we used fix them.

Note: the demos in this blog post require IE 9+ (preferably 10), Firefox, or any WebKit browser. If you’re on a mobile device, you might need to click the open in new page button to view the demos properly.


Building the 3D Demo

The Crocodoc 3D Demo separates a document into discrete layers, which you can rotate so that you can see how the document is constructed. Not only does it look pretty cool, but it gives some insight into the processing Crocodoc is doing on each document that we convert to HTML.

For the purposes of this blog post, I broke the demo down into three steps: building a basic proof of concept (without SVG), getting it to work in our target browsers, and finally, adding the SVG layers.

Just want to play with some code? Fork the 3d-demo repo!

Step One: Proof of Concept

When starting the Preview project, the first step was to create a proof-of-concept for the 3D demo. The project needed to work on the target browsers (IE 9+, Firefox, WebKit, and most mobile browsers). I have worked with CSS 3D transforms in the past, but not to the extent that this project required, so it was time to do a bit of research! The WebKitCSSMatrix class that I discovered seemed like a good place to start. Fortunately, the proof-of-concept demo was actually quite simple to build using the WebKitCSSMatrix class. Let’s take a look at some code!


The Page

The Page class inserts layer divs into a jQuery-wrapped container element, and exposes a rotate(dx, dy, dz) method, which rotates the page in 3D space. Each layer div is positioned LAYER_SPACING apart on the z axis. From here, it’s easy to take it a step further, and add mouse/touch controls for rotating the page.

Demo #1: The Page


Adding Transitions

In browsers that support CSS transitions, adding transitions is very simple, and done almost entirely in CSS. The JavaScript changes are mainly adding and removing CSS classes. We can also add a nice implode/explode effect.

Demo #2: Transitions


Step Two: Browser Support

Awesome, we have a working, nice-looking proof-of-concept! Now, how do we get it to work on all browsers? It turns out, currently the only two implementations of W3C CSSMatrix interface (2D and 3D) are WebKitCSSMatrix (supported by at least Chrome, Safari, iOS, and Android) and MSCSSMatrix (IE 10 only). What now? Some googling lead me to a CSSMatrix shim, which looked promising, so I gave it a go. Unfortunately, the shim was actually broken when I found it, but hey–a chance to contribute to a useful open-source project? Sign me up! Long story short, after several hours of poring through Wikipedia articles and even the WebKit source, I finally got it working properly (for a browser-ready version to play with, check out the browserified branch and add CSSMatrix.js to your page).

CSSMatrix Example


With Firefox support handled, it’s time to tackle IE 9. Even though IE 9 can’t render matrix3d values in CSS, we can compute them in JavaScript and truncate a few numbers… which is exactly what we did. And it looks way better than you might expect! Check out the demo below to compare how it looks in IE 9 (which we’ll call affine mode) vs proper 3D mode.

Demo #3: Affine Mode

(NOTE: can’t tell the difference between the two modes? you’re probably using IE 9. No? It’s also possible that hardware acceleration is disabled on your machine, which you can check in chrome://gpu/.)


IE 9 also doesn’t support CSS transitions, but I’m not going to go into detail about how we did that, because there are already a lot of jQuery plugins out there that make it pretty seamless. (Here are a few places to start: jQuery.transition.js, jQuery-Animate-Enhanced.)

Now that the demo works well in all the browsers we’re targeting, we can just throw in the SVG file and we’re done! Right? Well, not exactly.


Step Three: Make it Work with SVG

After a bit of (failed) experimenting, I realized that, out of the box, SVG does not support CSS transforms with perspective. My solution was to load the SVG with AJAX (really CORS from AWS S3, which required yet another IE shim, and a cache-related hack for Chrome), fix linked assets (Crocodoc SVG links to assets relatively), apply a filter to split the SVG into several distinct SVG objects, and embed each object using HTML5 Inline SVG in a separate div elements. Voila, we have our layers!

Demo #4: SVG Layers


Since the SVG is now contained in HTML elements, we can apply CSS 3D transforms to the containers, and everything works swimmingly. Except in IE 9. Yep, IE 9 supports inline SVG, but it doesn’t support document.importNode(), which is necessary for creating the inline SVG in the first place. I found a nice shim, which I modified slightly, because it was producing some issues with namespaced attributes.

If you’re interested, also check out demos 5, 6, and 7 (warning, you might need a fast machine).

Note: I also ran into some very troubling issues with Firefox.  Rotating the 3D demo would reproducibly cause a kernel panic on my MBP (Retina, Mid 2012). I unfortunately haven’t been able to create a reduced test case for this, but I’ll be sure to post about it when I do. It has something to do with applying 3D transforms to elements (possibly specifically SVG) in a overflow:hidden container.


Wrap Up

It’s true that SVG is still in its infancy in HTML, but I think our preview page alone proves that it’s already possible to do some incredible stuff. I hope this post inspires some great applications built on the new Crocodoc.

In my next post, I’ll explain the other demos on our preview page, as well as a few other issues we ran into.

- Cameron Lakenen, Crocodoc Developer

Interested in what we’re up to? Come join us! Check out our available positions now.

New: The Annotation Sidebar

Oct. 30 2012

A popular feature of our consumer product, Crocodoc Personal, is the annotation sidebar, which makes it easy for users to view and quickly navigate to any annotation within a document. We’re pleased to announce that Crocodoc’s platform product now incorporates an annotation sidebar of its own, which can be toggled via our API.

The sidebar helps track collaboration by multiple users across a document. Using the sidebar, users can view comments and nested replies, jump to any annotation within a document, and see which annotations were created when and by whom.

Annotations are organized by type, time of creation, and page:

The annotation sidebar is currently hidden by default, so if you’d like to enable it in your integration you can set a variety of options when creating a session. We recommend that developers use the sidebar’s “auto” setting, which dynamically displays the sidebar once annotations have been added to a document. You can read more about the different options in our documentation.

If you have any questions or recommendations, please don’t hesitate to contact us at

Crocodoc at the HTML5DevConf

Oct. 15 2012

Earlier today, Crocodoc’s co-founder Peter Lai presented a talk entitled “Rendering Documents in the Browser using HTML5”. He walked the crowd through the Crocodoc approach to the challenge of viewing documents in the browser, and made case for HTML as the best solution available for document embedding. Interested in learning more? You can catch us at our booth on the 2nd floor of the conference tomorrow, or drop us a line at if you’d like to invite Peter to speak at your conference or Tech Talk!

The HTML5 Page Flip Experiment

Oct. 8 2012

We’re constantly thinking about ways we can improve how documents are rendered in the browser. Along the way, we’ve run a number of technical experiments to explore what’s possible using the latest HTML5 features like SVG. Today we’re excited to explore one of these endeavors, the HTML5 Page Flip experiment. Here, Peter digs into some of his favorite HTML5 effects, and walks through just how to use SVG to enable smooth animations without the use of flash. Check it out now »

New: Mobile Annotations, Speed Improvements, and More

Sep. 24 2012

Since the release of our new API in May, we’ve been working on a number of improvements to our viewing experience including:

  • Mobile Annotations
  • Even faster conversion times
  • Improved rendering fidelity

Mobile Annotations

We’re always working on improving the mobile experience for Crocodoc users and are excited to announce that annotations can now be viewed on mobile devices. Users can add comments, reply to comments, and arrange previously created annotations.

When you first open the Crocodoc viewer on your mobile device, you’ll notice that the toolbar now has a comment button:

If you tap the comment button, you’ll see instructions for adding your first comment. From our new mobile interface, you’ll also be able to edit, delete, and reply to existing comments:

We’re constantly working on mobile support and would love to hear what you think of these new features. Let us know by emailing!

Crocodoc: Now up to 30% Faster

Through a combination of performance optimizations and infrastructure improvements, we’ve decreased average PDF conversion times from 3 seconds to 2 seconds. We know every second counts to your customers, so speed continues to be one of our top priorities.

Improved Rendering Fidelity

We’ve made a few updates that have improved rendering accuracy and user experience:

  • Glyph encodings: Some symbols and other special characters that were previously unavailable now display properly across all document types.
  • Unicode processing: Copy and pasting text is even more accurate, both for standard and non-standard/non-Latin characters.
  • Font hinting: Font hinting, which relates to the sub-pixel rendering of text, has been improved for Windows— your text will render more accurately on the page.
  • Image processing: Embedded images are clearer, more precise, and load faster.