Exporting Canvas to HTML (and PDF)

I developed a script to export a canvas to HTML. Then you can open the HTML file in your browser and print to PDF. Any external links in your canvas will be clickable in the PDF.

First, open your canvas in Obsidian and zoom/pan to whatever view you want to export. Then open Developer Tools and run this script:

// Get a copy of the canvas content element
let view = app.workspace.activeLeaf.view;
if (view.getViewType() !== "canvas") {
  throw new Error("The active view is not a canvas");
}
let content = view.contentEl.cloneNode(true);

// Remove the canvas background dots
content.querySelector(".canvas-background").remove();

// Remove the canvas UI controls
content.querySelector(".canvas-card-menu").remove();
content.querySelector(".canvas-controls").remove();

// Remove the canvas node labels (image filenames)
content.querySelectorAll(".canvas-node-label").forEach((el) => el.remove());

// Get all the CSS, except for print styles
// https://developer.mozilla.org/en-US/docs/Web/API/StyleSheetList#get_all_css_rules_for_the_document_using_array_methods
let allCSS = [...document.styleSheets]
  .map((styleSheet) =>
    [...styleSheet.cssRules]
      .map((rule) => rule.cssText)
      .filter((cssText) => !cssText.includes("@media print")) // No print styles
      .join("\n")
  )
  .join("\n");

// Global regex matches app:// followed by any characters except /
let pattern = /app:\/\/[^\/]*/g;

// Generate HTML & CSS. Remove any app:// prefixes from URLs.
let html = `
<!DOCTYPE HTML>
<html>
<head>
<style>
${allCSS}
/* Use exact colors for card backgrounds and bullets */
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
</style>
</head>
<body class="${document.querySelector("body").className}">
${content.outerHTML}
</body>
</html>`.replaceAll(pattern, "");

// Save html file
let filename = "canvas-export.html";
existingFile = app.vault.getAbstractFileByPath(filename);
if (existingFile) {
  app.vault.delete(existingFile);
}
app.vault.create(filename, html);

console.log("Open this file in your browser and print to PDF:");
console.log(`${app.vault.adapter.basePath}/${filename}`);
18 Likes

thank you so much for this! I would love to see this natively included. it would be nice if the HTML included hyperlinks and website iFrames too

4 Likes

Thanks for your sharing.
May I ask how to use these scripts in the developer tool in Obsidian? I can see 4 menu items including Elements, Console, Source and Network, but don’t know how to use the scripts.

1 Like

Just paste it in the Console tab of the dev tools.

2 Likes

Where can I access the development tools

Just tried this and it threw an error: “uncaught Error: The active view is not a canvas at :4:9”

I’m on a Mac, if that makes any difference. Here’s what I did:

  1. I loaded the canvas in obsidian
  2. chose “Toggle Developer Tools” from the View menu
  3. pasted the above code in the Console, and hit return.

I think those are the right directions. Is there some step I’m missing (or is there any other way to get a canvas to html/pdf)?

thanks.

Try resizing the canvas before running the script (I shrank until everything was visible on one screen), that did the trick for me after running into said error on a Mac.

Really nice script :heart: thank you! Any chance to get this as a obsidian plugin, with some auto export functionality?

This is really great!!

I slightly modified the original script and set the following as a QuickAdd Macro. Now I can use it as an Obsidian command!

Also, now the exported file path is automatically copied to the clipboard.

(P.S. I tried to open the resulting HTML without saving it to a file, but had no luck…)

const DEFAULT_EXPORT_PATH = "Plugins/Canvas/export.html";

module.exports = {
    entry: async (params, settings) => {
        const {
            app,
            quickAddApi,
            obsidian: { Notice },
        } = params;

        // Get a copy of the canvas content element
        let view = app.workspace.activeLeaf.view;
        if (view.getViewType() !== "canvas") {
            new Notice("The active view is not a canvas", 5000);
        }
        let content = view.contentEl.cloneNode(true);

        // Remove the canvas background dots
        content.querySelector(".canvas-background").remove();

        // Remove the canvas UI controls
        content.querySelector(".canvas-card-menu").remove();
        content.querySelector(".canvas-controls").remove();

        // Remove the canvas node labels (image filenames)
        content.querySelectorAll(".canvas-node-label").forEach((el) => el.remove());

        // Get all the CSS, except for print styles
        // https://developer.mozilla.org/en-US/docs/Web/API/StyleSheetList#get_all_css_rules_for_the_document_using_array_methods
        let allCSS = [...document.styleSheets]
            .map((styleSheet) =>
                [...styleSheet.cssRules]
                    .map((rule) => rule.cssText)
                    .filter((cssText) => !cssText.includes("@media print")) // No print styles
                    .join("\n")
            )
            .join("\n");

        // Global regex matches app:// followed by any characters except /
        let pattern = /app:\/\/[^\/]*/g;

        // Generate HTML & CSS. Remove any app:// prefixes from URLs.
        let html = `
    <!DOCTYPE HTML>
    <html>
    <head>
    <style>
    ${allCSS}
    /* Use exact colors for card backgrounds and bullets */
    body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    </style>
    </head>
    <body class="${document.querySelector("body").className}">
    ${content.outerHTML}
    </body>
    </html>`.replaceAll(pattern, "");

        // Save html file
        let filename = settings["Export path"] ?? DEFAULT_EXPORT_PATH;
        let existingFile = app.vault.getAbstractFileByPath(filename);
        if (existingFile) {
            await app.vault.modify(existingFile, html);
        } else {
            await app.vault.create(filename, html)
        }

        await quickAddApi.utility.setClipboard(`${app.vault.adapter.basePath}/${filename}`);
        new Notice("Path copied to clipboard!", 5000);
    },
    settings: {
        name: "Export Canvas to HTML",
        author: "rrherr",
        options: {
            "Export path": {
                type: "text",
                defaultValue: DEFAULT_EXPORT_PATH,
            }
        }
    }
}
3 Likes

On this topic, I know I’m still waiting for a native option to become available as well but one idea that I should be considered for this is to base it off Excalidraw’s Slide Script. This was really effective for quickly turning the [excalidraw] canvas to a presentation, it seems like it could also be used to then print out an export.

One addition I would make is the ability to have a set frame that sets the aspect ratio appropriately… perhaps even printing by card order. For example, here is a high-level view of a presentation I recently gave using block note and I would like to print it out with an infinite vertical scroll by essentially identifying the order of blocks.

Thanks for the real useful script !! Do any one of you might know how to export the canvas by keeping links to the notes ? (i created my canvas by linking notes together, and i’d like to open the .md file when clicking on the nodes of the exported canvas.html)

Awesome! THanks!

@rrherr: Thanks for the script – excellent work and very useful.

Thx for sharing.

Is there a quality difference between canvas export → .PNG img → print to PDF & canvas export → .HTML file → print to PDF ?

(except for the small dots in the background for png export (which can perhaps be removed?))

To convert a canvas to a document (which is then convertable to PDF etc.) with all card, nodes, media and all canvas connections as internal links check the Canvas2Document plugin: GitHub - slnsys/obsidian-canvas2document: Plugin for Obsidian to convert a complete Canvas to a long form document