From 44b3cfe6de47d983867722dd31634d934b8bc360 Mon Sep 17 00:00:00 2001 From: Kuba Orlik Date: Wed, 22 Dec 2021 19:32:19 +0100 Subject: [PATCH] Add support for inline images --- mail-to-pdf.mjs | 92 +++++++++++++++++++++++++++++++++++++---------- package-lock.json | 11 ++++++ package.json | 1 + 3 files changed, 86 insertions(+), 18 deletions(-) diff --git a/mail-to-pdf.mjs b/mail-to-pdf.mjs index 6e978c7..59d9092 100755 --- a/mail-to-pdf.mjs +++ b/mail-to-pdf.mjs @@ -1,5 +1,6 @@ #!/usr/bin/zx --quiet +var elparser = require("elparser"); import { resolve, basename } from "path"; const pwd = (await $`pwd`).stdout.replace("\n", ""); @@ -37,22 +38,46 @@ let html = `; +if (!html.includes(" + + + + ${html} + `; +} + +if ( + html.includes( + `` + ) +) { + html = html.replace( + ``, + "" + ); +} + +if (!html.includes(`", ``); +} + const attachments = (await $`mu extract ${path}`).stdout; -function getSimpleHeader(body, header_name) { - const regexp = new RegExp(`:${header_name} (.*)\n`); - console.log("get simple header", header_name, body); - return body.match(regexp)[1]; +async function emacsEval(expr) { + return (await $`emacsclient --eval ${expr}`).stdout.slice(0, -1); // slice to remove trailing newline; } -function formatAddress(sexp) { - sexp = sexp - .replace(/(^\(\(|\)\)$)/g, "") - .replace(/"? \. "/, " <") - .replace(/"$/, ">") - .replace(/^"/, "") - .replace(/^nil <(.*)>$/, "$1"); - return sexp; +async function getSimpleHeader(body_sexp, header_name) { + return await emacsEval(`(plist-get '${body_sexp} ':${header_name})`); +} + +async function formatAddress(sexp) { + const [name, address] = await Promise.all([ + emacsEval(`(car (car '${sexp}))`), + emacsEval(`(cdr (car '${sexp}))`), + ]); + return `${name === "nil" ? "" : name} <${address}>`; } function escape(str) { @@ -120,14 +145,45 @@ html = html.replace( ` ); -const subject = getSimpleHeader(mail_body, "subject").slice(1, -1); // slice to remove " -const from = formatAddress(getSimpleHeader(mail_body, "from")); -const to = formatAddress(getSimpleHeader(mail_body, "to")); -const date = await formatDate(getSimpleHeader(mail_body, "date")); +const subject = (await getSimpleHeader(mail_body, "subject")).slice(1, -1); // slice to remove " and newlines +const from = await formatAddress(await getSimpleHeader(mail_body, "from")); +const to = await formatAddress(await getSimpleHeader(mail_body, "to")); +const date = await formatDate(await getSimpleHeader(mail_body, "date")); +const parts = elparser + .parse1(`'${await getSimpleHeader(mail_body, "parts")}`) + .qsexp.list.map((entry) => + Object.fromEntries( + entry.list.map((value, index, array) => { + if (index % 2 == 0) { + const key = array[index].symbol.slice(1); + let value = array[index + 1]; + for (const subkey of ["string", "symbol"]) { + if (value[subkey] !== undefined) { + value = value[subkey]; + } + } + if (value.type && value.type?.type === "int") { + value = parseInt(value.val); + } + return [key, value]; + } else return []; + }) + ) + ); + +const inline_attachments = parts.filter((e) => e.cid !== undefined); + +for (const inline_attachment of inline_attachments) { + await $`mu extract --overwrite ${path} ${inline_attachment.name} --target-dir=/tmp --overwrite`; + html = html.replace( + `cid:${inline_attachment.cid}`, + `./${inline_attachment.name}` + ); +} html = html.replace( //i, - `` + + `` + /* HTML */ `

${subject}

@@ -159,7 +215,7 @@ const output_file = `${resolve(path, "../")}/${basename(path)}.pdf`.replace( " " ); -await $`wkhtmltopdf ${output} ${output_file}`; +await $`wkhtmltopdf --enable-local-file-access ${output} ${output_file}`; // --enable-local-file-access is there so the inline images don't have to be included as base64. Probably not the safest approach and base64 would be better // console.log(pwd + "output.html"); // console.log(mail_body); diff --git a/package-lock.json b/package-lock.json index 300a544..8627dbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "elparser": "^0.0.9", "sexpr-plus": "^7.0.0" } }, + "node_modules/elparser": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/elparser/-/elparser-0.0.9.tgz", + "integrity": "sha1-+bQxsVZSCK8roHKKE8Ux4FWbrSI=" + }, "node_modules/sexpr-plus": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/sexpr-plus/-/sexpr-plus-7.0.0.tgz", @@ -19,6 +25,11 @@ } }, "dependencies": { + "elparser": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/elparser/-/elparser-0.0.9.tgz", + "integrity": "sha1-+bQxsVZSCK8roHKKE8Ux4FWbrSI=" + }, "sexpr-plus": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/sexpr-plus/-/sexpr-plus-7.0.0.tgz", diff --git a/package.json b/package.json index 0229344..adf3f75 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "author": "", "license": "ISC", "dependencies": { + "elparser": "^0.0.9", "sexpr-plus": "^7.0.0" } }