Published
Edited
Aug 27, 2020
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
render = async ast => {
var doc = new pdf.Document({ font: fonts.Helvetica });

const renderInline = async (text, node, format) => {
switch (node.type) {
case "text":
return text.append(node.value.replace(/\n/g, ' '), format);

case "inlineCode":
return text.add(node.value.replace(/\n/g, ' '), {
...format,
font: fonts.Courier
});
case "strong":
for (let child of node.children) {
await renderInline(text, child, {
...format,
font: fonts.HelveticaBold
});
}
return;
case "link":
text.append(' ', format);
for (let child of node.children) {
await renderInline(text, child, {
...format,
link: node.url,
underline: true,
color: 0x569cd6
});
}
return;
case "image":
const req = await fetch(node.url);
const data = await req.arrayBuffer();
text.image(new pdf.Image(data));
return;

// TODO: Handle more inline nodes
}
};

const getDestination = node => {
if (node.value) {
return node.value.toLowerCase().replace(/\s+/g, '-');
}
return node.children.map(getDestination).join('-');
};

const renderNode = async (node, format, doc) => {
switch (node.type) {
case "heading":
var cell = doc.cell({ padding: 4, paddingBottom: 10 - 2 * node.depth });
var text = cell.text();

for (let child of node.children) {
await renderInline(text, child, {
...format,
fontSize: 18 - 2 * node.depth,
font: fonts.HelveticaBold,
destination: getDestination(node)
});
}
return;
case "paragraph":
var cell = doc.cell({ padding: 4 });
var text = cell.text();
for (let child of node.children) {
await renderInline(text, child, format);
}
return;

case "code":
var cell = doc.cell({
padding: 5,
padding: 5,
backgroundColor: 0xEEEEEE
});
var text = cell.text(node.value, {
font: fonts.Courier
});
return;

case "table":
var docCell = doc.cell({ padding: 10 });
var table = docCell.table({
widths: node.children[0].children.map(_ => 70),
borderWidth: 1
});
let i = 0;
for (let rowNode of node.children) {
var row = i++ === 0 ? table.row() : table.row();
for (let cellNode of rowNode.children) {
var cell = row.cell();
var text = cell.text();
for (let child of cellNode.children) {
await renderInline(text, child);
}
}
}
return;

case "listItem":
// TODO: Figure out how to add bullets/numbers
var cell = doc.cell({ paddingLeft: 10 });
for (let child of node.children) {
await renderNode(child, format, cell);
}
return;

default:
// TODO: Handle all block nodes
console.log(node.type);
if (!node.children) return;
for (let child of node.children) {
await renderNode(child, format, doc);
}
return;
}
};

await renderNode(ast, {}, doc);
return doc;
}
Insert cell
pdf = require('https://bundle.run/pdfjs@2.4.1')
Insert cell
amfFont = require('https://bundle.run/pdfjs@2.4.1/lib/font/afm.js')
Insert cell
Insert cell
Insert cell
markdown = require('https://wzrd.in/standalone/remark@latest')
Insert cell
Insert cell
Insert cell
ast = markdown.parse(input)
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more