Skip to content

Building content services

James Adam edited this page Aug 9, 2023 · 6 revisions

While all the software architecture is really nice and all that, it's all pointless without engaging, interesting, useful and fun content to be printed.

(If you haven't already looked at the general system architecture, please take a look; it will make some of this make more sense.)

The beauty of this software is that it's very simple to turn content into printouts; you just need to produce some HTML, and the printer system will take care of the rest. But even with that said, there are a few things to be aware of that will make your life easier.

Printing from a static page

The simplest possible content service isn't even an application -- it's just a static HTML page that includes some Javascript from the printer backend.

The backend provides one such page, called sample.html

<!doctype html>
<html class="no-js" lang="en">
  <head>
    <script src="http:https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <!-- 1. --> <link rel="stylesheet" href="http:https://printer.exciting.io/stylesheets/print.css" type="text/css" media="screen" title="no title" charset="utf-8">
    <!-- 2. --> <script src="http:https://printer.exciting.io/javascripts/printer.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript" charset="utf-8">
      $(function() {
        $("#previewPage").click(Printer.previewPage);
        $("#printPage").click(function() {
          var printerID = prompt("Enter the ID of the printer to target");
          Printer.printPage(printerUrl, function(result) {
            if (result.response == "ok") {
              alert("Page successfully sent for printing");
            } else {
              alert("There was a problem sending this content");
              console.log("Error response", result);
            }
          });
        });
      })
    </script>
  </head>
  <body class="preview"> <!-- 3. -->
    <div class="controls">
      <a id="previewPage" href="#">Preview</a>
      <a id="printPage" href="#">Print</a>
    </div>
    <div class="paper"> <!-- 4. -->
      <div class="content">
        <h1>Your content</h1>
        <p>(Put it here, yo.)</p>
      </div>
    </div>
  </body>
</html>

There are a few key features:

  1. including the print.css
  2. the printer.js javascript
  3. the preview class on the body element
  4. the paper and content divs.

print.css and page width

Little receipt printers have a very small page width -- normally 384 pixels. The backend server tries as hard as it can to constrain the width of the content for printing to fit in this space, but at the moment it's still possible for a page to accidentally grow to a greater width, which will cause printing to fail.

The print.css stylesheet provides a few small styles to help constrain your content to 384px. All you need to do is wrap your content in an element with the content class, and this will be set to the correct width.

I strongly recommend that you don't put any other content on your page outside of the content element (with the preview caveat I'll explain below.

Previewing

Designing inside a plain white div isn't very inspiring, and also doesn't communicate anything about the margins which will be present on the final printout.

To help give a more realistic preview, print.css also includes a style for a paper element, which is intended to wrap around the content element described above, like so:

<div class="paper">
  <div class="content">
    <h1>Your content</h1>
  </div>
</div>

If you give the body element of your page the preview class, your content will then appear centered and with approximate margins, making it much easier to design. When the backend server performs its rasterisation, it removes this preview class, reverting the page back to its plain, unadorned version.

You can also use this preview class to show elements while designing (such as the Javascript print controls which we'll discuss below), but hide those elements when preview is not present.

Sending content via Javascript

Since there's no way for the printer backend to request your local HTML page, we can send the content to it using the javascript in printer.js. This provides two functions -- Printer.previewPage and Printer.printPage -- which you can connect to links or buttons on your page.

(If you are running your own print backend, or a local one, you can also change the value of Printer.backendUrl to point to your server. The default is http:https://printer.exciting.io.)

The Printer.previewPage function simply posts the entire content of the current page to the backend server, and then redirects you to the preview page (which will reload until the preview is ready). This is a good way of checking how fonts will appear during the rasterisation process.

The Printer.printPage expects a printer URL (e.g. http:https://printer.exciting.io/print/<your printer id>) and a callback, which is passed through to jQuery's ajax mechanism. It also posts the page content to the server at Printer.backendURL.

In this way, you can design, preview and test-print HTML without needing to deploy any content to publicly-accessible servers.

Building a simple application

Printing from a static HTML page is great, but it's not exactly interesting. Much better would be to have an application that other people could interact with to produce printouts!

The printer-mail application is a very simple example of such an application. It lets people send 'messages' to other people's printers.

The README on github gives a reasonable overview of how the software works, but in a nutshell there are two distingushing features from our simple page above:

  • The application stores print URLs
  • The application posts a url to the backend, rather than HTML content

Storing print URLs

In order to use the application, printer owners must "register", which in this case means finding out your print URL (if you have a printer connected to the printer.exciting.io backend, you can find your URL by visiting http:https://printer.exciting.io/my-printer), and then using a form to store this along with a "nickname" in a small database. This nickname is used to retrieve the print URL when friends visit the application.

For example, if I people to send messages to "lazyatom", and my print URL was http:https://printer.backend.server/print/abc123, then I'd use both those values in the registration.

Posting urls for printing

When the friend of a printer owner sends them a message, the content of that message is saved by the application, and associated with the nickname/print URL record. Immediately after saving this data, the application sends a request to the print URL in roughly the following form:

POST http:https://printer.backend.server/print/abc123
     url=http:https://printer-mail.server/message/457345

The url parameter corresponds to a page in the mail application which will display the sent message in a suitable manner (width, styling, as described above) to be printed.

The backend printer will then request that URL, download the content and get busy with preparing it for the printer connecting to it with the ID abc123.

That's pretty much the whole application, in a nutshell. The printer-paint application follows almost exactly the same pattern (much of it was entirely copied).

It's really a trivial demonstration, and if you were really interested in producing something similar you would probably want to add things like robust validation, checking for duplicate registrations, removing your printer, restricting who could send you a message, and so on.

Scheduled content

Printing when someone presses a button is a bit more interesting than a static page, but it also doesn't quite reach the promise of waking up to a nice bundle of interesting and relevant content that's been automatically prepared for us.

Thankfully, it's only slightly more work to remove our friends from the printing procedure entirely and have the content be prepared and sent to the printer entirely automatically.

The printer-weather application is a very simple demonstration of exactly this. For every printer than is registered with it, a custom weather forecast is prepared and downloaded by the printer at 8am sharp, every day.

You can view the source to find out more about how it's put together, but there is only one significant difference between it and the mail and paint examples above:

  • automatic scheduling of print URL posting

As with printer-mail, the application requires you to "register" your print URL (although no nickname is required), and this is stored in a database. In order to give you an accurate weather forecast, the application needs to know where in the world you are, and so the simplest possible way to do that is to use geocoding to estimate your location from your IP. This is also stored against the print URL, and the record is given a unique ID.

Nothing else happens until the scheduler (which could be "cron", but in this case is the Heroku scheduler addon) wakes up at 8am GMT.

... and at 8am...

When the scheduler wakes up, for every record in the database, it sends a request:

POST http:https://whatever.printer.backend/print/:printer-id
     url=http:https://weather.service.host/jobs/:job-id

When a backend server requests http:https://weather.service.host/jobs/:job-id, the appropriate record is loaded from the database, and the IP is used by the (extremely hacky) weather code to ultimately produce some weather data, which is finally rendered in HTML (with icons drawn in HTML5 canvas tags -- fancy).

The backend then dutifully consumes the content and hands it off the appropriate printer as before.

Were this a more robust application, it would ask you for your location as part of the registration, validate it and store that against the print URL, rather than the IP. It could also store your name and use that in the printout to be friendly.

Beyond these toys

The point is simply that an application could store whatever data it wants with a print URL, and use that to produce interesting and useful content. A very similar service could as easily be loading todo lists by obtaining a Google data token on behalf of the user, or printing out the latest twitter mentions by storing a twitter OAuth token.

The only limit is your imagination and creativity, and your ability to turn that into HTML.

Now get to it!