Saving Document as PDF

I can see that there is functionality for exporting individual cells as SVG or PNG, but I have a use case where I’d like to print an entire notebook as a PDF. It has no code, only images and markdown text.

Specifically, I’ve prepared lecture notes using the template here, but now I’m getting requests for PDF versions of the slides, so that they can be read offline. Printing from a browser is splitting images across pages.

Is there some functionality that I’m not aware of / has anyone found a workaround?

3 Likes

The simplest way would be to save the document as PDF, e.g. via the print dialog. Some observations:

  • The notebook iframe cannot trigger a print dialog:

    Ignored call to 'print()'. The document is sandboxed, and the 'allow-modals' keyword is not set.

  • The parent window cannot trigger a print dialog on the iframe:
    > frames[0].print()
    VM5624:1 Uncaught DOMException: Blocked a frame with origin "https://observablehq.com" from accessing a cross-origin frame.
    
  • page-break-* declarations inside the iframe have no effect.

I have a hard time imagining how printing (or a conversion via the parent window) could work under these conditions. My recommendation would be to instead generate a PDF on the fly for download, e.g. via html2pdf.

If you provide a link to your notebook maybe I (or others) will be able to give some pointers.

That’s a good idea about a link, here is one example: https://observablehq.com/@krisrs1128/introduction-to-inference

The biggest problem the students have described is the page breaks. I’ll check out html2pdf and report back on whether this works.

1 Like

Also, if the notebook has a cell in waiting state, chrome will hold on preview, this is a problem when we have a file input or other cells that are intentionally left in waiting state.

Can you provide a demonstration (or describe the steps to create one in detail)? I would think that Chrome should have no concept of Observable’s pending cell states. Are there perhaps other constant DOM changes, like a loop that yields an element on every animation frame?

https://observablehq.com/@a10k/zinzi in this notebook, but when I comment the second cell with widget… the print works… Thanks for looking into this.

Also, on a related note, the width value in my notebook is 954 but when I print, its 272. Is there something in the settings I should tweak? (I’m on windows 10, chrome Version 84.0.4147.135 (Official Build) (64-bit))

I suspect that it has something to do with how Vega (Vega-Lite) calculates layouts, which may conflict with Chrome’s page splitting and cause the page generation to take unusually long. I wasn’t able to narrow it down though.

The pixel width of a print page depends both on the selected output format (paper size and orientation) and the device pixel resolution reported by the OS. You can check the resolution with:

{
  const n = html`<div style="width:1in">`;
  yield n;
  yield getComputedStyle(n).width;
}

or measure the size of a supposed 1x1 inch square yourself:

html`<div style="width:1in;height:1in;background:red">`
1 Like

@mootari
Using the notebook @mootari/printable to save the document as pdf, I am getting the message “Notebook not found or inaccessible” when the notebook is not public.
Is there any access control related to the url to be retrieved or not controlled by Observable?

Checking the code, the data.url is retrieving 404 when the notebook is not public

fetchMeta($input.value, fetchMetaOptions)
.then(data => {
const path = data.url ? new URL(data.url).pathname.slice(1) : null;
if(data) {
Object.defineProperty(data, ‘toString’, {value: () => path});
}
$form.value = data;
$form.dispatchEvent(new Event(‘input’));
})
.catch(e => {
setValidity(‘Notebook not found or inaccessible.’);
})

@e.roberto Unfortunately your notebook needs to be at least unlisted, as the APIs used are limited to public access.

@mootari Is it possible to copy the code from the APIs used to a copy(fork) of printable notebook that is not public so that we can save the not public notebook as pdf ?

Technically you could provide the contents from both API calls, but the process would be so cumbersome for every single notebook that I don’t want to spend time adding support for it.

1 Like