Easily Print or Capture Web Viewer content

By Daniel Wood, 25 September 2019

Introduction

Back in 2008 when Digital Fusion released Reactor, we used web viewers a lot and we printed them a lot too. Display of javascript based gantt charts, calendars and other rich UI widgets would inevitably require printing by clients and so we'd come up with correctly sized print layouts to do the job.

That was the pre FileMaker 16 days, dating back to the introduction of web viewers in version 8.5. Printing web viewers back then worked pretty darn good.

Then in FileMaker 16 things changed. All of a sudden web viewers that render content fine in browse mode, would fail to render when in Preview mode, printed, or saved as PDF. The exact reasons for this change are murky and unclear.

printwv 2 

These days, javascript is everywhere and more popular in FileMaker than ever. It therefore makes sense that people are going to want to be able to print the content they build for the web viewer.

In this article we cover a really simple and easy technique you can quickly add to your own solution to start printing web viewer content.  No plugins are needed and no external OS level scripting or screen capturing required.

 

It's an API, duh!

Is anything these days not solvable with an API? I like to think not. When looking for a solution to a problem such as this, my first option is to look for an API that can do the job. So when looking for an API to solve this issue I looked for one capable of rendering html content.

The results took me to phantomjscloud.com.

PhantomJsCloud is actually a spinoff of an earlier project named PhantomJS. This was a headless browser API allowing testers to carry out user interactions on a website through javascript. Developers would write code to simulate user actions on a site.  This project has since been discontinued.

PhantomJsCloud is a cloud based variant based on the same principle. It has a restful API where you can request the API to render a website and return the rendered site as an image, pdf, text or json.

Where PhantomJsCloud is really useful for us as FileMaker developers is its ability to render specific html code you specify, and it is this feature we use to print the web viewer.

 

The basic steps involved

Setup of the API is really easy. The API is free for up to 500 calls per day - more than enough for most people. With it you get an API key that you use in each request. There is no additional authentication required.

Let's say you wanted to print a web viewer that contained a javascript based chart such as the one below:

printwv 3

The steps involved to print are:

  1. Obtain the code used in the web viewer. This is usually stored in a field and rendered in the web viewer with a Data URL such as "data:text/html,". Alternatively you can still use a URL to render if your web viewer is displaying a site.
  2. Sign up to phantomjscloud.com and receive your API key
  3. Using the Insert from URL script step, send a POST request to the API with your code/url.
  4. The API will return you an image of the rendered code/site. Place this in a container field
  5. Print the container field image.

That's it in a nutshell. We'll go into a little more detail as to how the setup and post request is constructed, but that is really all there is to it.

I encourage you to download and try out the example file for this article.  You can use it to follow along with the rest of the article.

 

Sign up to the API

Simply go to phantomjscloud.com and sign up for an account.  Verify your email address, and when you sign in on the dashboard screen you'll find your API key, that's really all there is to this part. 

If you require more than 500 requests per day, there are pre-pay options or pricing plans available, but we think most developers won't need more than 500 a day.

 

printwv 4

Gather your web viewer code

There are so many different ways to render code in a web viewer. Some like to use a Data URL to render html content stored in a field or variable. Others might like to export their files to disk and reference them via the web viewer URL. Others might  use a plugin to render content.

Whichever method you use, you're going to need your html code to be available to you in a script so that you can pass it to the API. If you simply display a standard URL in your web viewer than you are all set.

The API can accept either html code, or a URL to render.

printwv 5

using code in a field, rendered using a data URL. 

Formulate your request to the API

The API request is a POST, and for this we'll use the "Insert from URL" script step, and some CURL parameters.

The URL for the request is pretty simple, it's the PhantomJsCloud site URL with the API key included:

printwv 6 

The request is formulated as JSON, and sent as data in the CURL part of the request as a POST.

In our example file we have done the heavy lifting for you and written a standalone script you can use in your own solution. In it we've included the basic request parameters used, these include:

  • Viewport width/height - this tells the API what sized "virtual" browser window it should use to render and capture the content in. This is essentially how you tell the API what size image you want.
  • Render format - you can choose png, jpg, text or json. For the example solution we've included png, jpg and pdf.
  • Render output quality - for jpg images you can specify quality between 0 and 100.

When it comes specifying what to render, the two key json key parameters are "content" and "url".  If you are simply rendering a URL, you specify the URL, and content can be empty.

If however you specify content, then the content parameter will include your html code. The URL parameter must be set to "http://localhost/blank" for it to work.

We send the request as a POST using CURL options as shown below. The $body variable contains our JSON request.

printwv 7

Store the response

The response from the API is typically an image (unless you specify the text or json options). So you simply set the result from the Insert from URL step into a container field.  When done, now you have an image in a container that you are able to print however you wish, and that's all there is to it!

 

An alternative use case - SVG image conversion

Rendering code as an image using an API is a really novel way to render the web viewer. But there are other interesting applications to this API.  One of which solves an issue I had recently when developing the Elemental solution. In this solution some of the APIs I integrated with would return the result as an SVG image. This meant that the SVG was unable to be rendered in a container field, and so for those examples, the image had to be rendered in a web viewer only.

printwv 8

This meant the image - in this case an avatar - could not be easily used by a developer. It couldn't be rendered in a portal (web viewers don't render in portals), and so the only option is to convert.  In Elemental, my solution was a clunky web based method that was unreliable. It involved rendering there SVG in a canvas object in a web viewer, then converting the canvas to base64 encoded PNG. In order to extract the PNG out of the web viewer, a callback URL to a FileMaker script was used and the base64 encoded image passed to the script as a parameter. However there were 2 major issues:

  • FMP urls have a size limit on parameters, so larger SVG's did not work
  • The overall technique was clunky, and didn't work on windows and was unreliable.

The technique was abandoned. However in using this technique, I realised this actually solves the SVG conversion issue really nicely, here's how:

  • Render the SVG in a web viewer inside a div element.
  • Use some CSS styling to size the div element to the size of the web page. This allows the SVG to dynamically scale to be the same size as the websites view portal.
  • Now just run the code through the API and return the result as a PNG image.

In the example file for this article you'll find this exact technique as a working example. It can be used to convert any SVG of any size or complexity to png or JPG quickly and reliably.

 

Other functionality of the API

We've used the API purely for rendering a site as an image to suit our needs. However the API is pretty cool and does some other potentially useful things, such as:

  • Return a sites text content only, useful for extracting content out of a site
  • A separate automation API that can be used to programmatically interact with a site or content. This can be useful if you require your code to have user actions applied before it is rendered and captured by the API.
  • Zoom a page prior to capture
  • Render a site a a thumbnail - great if you want to capture a company site as a preview thumbnail, it uses smarts to extract best look of a site.
  • Generate a PDF of a web page - this is awesome! If your web viewer contains a large amount of data, or is wide/deep in terms of scroll depth, you can use this to produce a PDF render of its entire contents, something you can't do with a straightforward image capture.

 

A warning about security

You must take caution when using any API, especially if you are sending it sensitive or private confidential information in your site or code. Any information you send to the API could be stored on the servers at their end, and so make sure you only use this for generic purposes where you are not using information you wish to keep private.

If security is a concern, then consider purchasing phantomJsCloud enterprise plans. These are private cloud instances dedicated to your solution.

 

In conclusion

We've found this to be a really easy to implement and reliable way to render web viewer content generated directly from FileMaker. The API takes around 1-2 seconds on average to return the rendered image (usually 30-40kb in size). Other methods we tried involved plugins that screen capture the web viewer part of your screen. The issue here is around resolution and size of the capture, it is quite limiting. The API gives fuller flexibility around this. Not to mention no plugin is needed. This one is definitely worth checking out.

 

Example File

We always strive to provide the best example file to help you understand the article and get a head start on implementing techniques found therein. Please download and try it out for yourself below:

Click to download the example file here. 

 

 

Something to say? Post a comment...

Comments

  • Tim Anderson 13/09/2023 8:59pm (7 months ago)

    This is great and I am sure I will be able to use it, but doesn't work for my first use case.
    I am trying to create images from a PivotTable installed using Carafe. The api always renders the default table that is returned, it ignores any modifications of included/excluded data or table type.
    I suspect this is down to the changes being managed in javascript not in the web viewer itself. I have tried using the code and the web viewer name - both behave the same.
    Do you know of any way I could capture modified reports and charts?

    Thanks

  • Daniel Wood 10/06/2021 11:19am (3 years ago)

    hi Jeff, thank you for the comment and apologies for the late reply.

    The only real advice I can offer here is to probably turn on script debugger and work through the script step by step, investigating the JSON built up and sent to the "capture web viewer" script, and after that whether that script itself is performing as expected. It may be something you are missing is there.

    Web viewers are expected to be named with an object name for reference so I'd make sure that is also done.

    Finally, you say an 'incomplete png' is returned, does this mean it's half rendered but not fully ?

  • Jeff Drake 28/05/2021 4:25am (3 years ago)

    Awesome stuff! However, I’ve got an insanely weird situation: your sample file works beautifully, but when I send the exact same code—to the character!—from my own file, it returns an incomplete png.

    By any chance, do you have any suggestions? (I’ve checked everything a colleague and I can think of, to no avail.)

  • Andreas Thyholdt 16/11/2019 10:34pm (4 years ago)

    Thanks, Daniel.
    This is going to be extremely useful to avoid all kinds of issues with printing Web Viewers. Much appreciated.

  • Daniel Wood 26/09/2019 2:25pm (5 years ago)

    hi Steve,

    Yeah you're quite right, I'm not sure what version that became implemented, but the data:text/html is not required, I think I'm just so used to always adding it in for backwards compatibility I tend to forget about that change. Thank you for pointing that one out :)

  • Steve 26/09/2019 10:47am (5 years ago)

    Thanks for the article. I seem to recall reading somewhere that the:

    "data:text/html,"

    is no longer required for Web Viewers, maybe since v16. Do you know anything about this change?

    cheers,
    Steve

  • Daniel Wood 25/09/2019 7:46pm (5 years ago)

    You're most welcome Jeff :)

  • Jeff 25/09/2019 4:47pm (5 years ago)

    thanks

RSS feed for comments on this page | RSS feed for all comments

Categories(show all)

Subscribe

Tags