pdf generation command needs an update

main
Zed A. Shaw 1 year ago
parent 0d6e8ecc90
commit ff85501af3
  1. 90
      commands/pdfpresgen.js
  2. 1
      commands/templates/emails/config.js

@ -1,12 +1,11 @@
// you may not need all of these but they come up a lot // you may not need all of these but they come up a lot
import fs from "fs"; import fs from "fs";
import assert from "assert";
import logging from '../lib/logging.js'; import logging from '../lib/logging.js';
import glob from "fast-glob";
import path from "path"; import path from "path";
import template from "lodash/template.js";
import { defer } from "../lib/api.js"; import { defer } from "../lib/api.js";
import PDFDocument from "pdfkit"; import PDFDocument from "pdfkit";
import assert from "assert";
import { mkdir_to, glob, changed } from "../lib/builderator.js";
const log = logging.create(import.meta.url); const log = logging.create(import.meta.url);
@ -14,7 +13,7 @@ const title_box = { x: 17, y: 415, width: 405, align: "center" };
const summary_box = { width: 405, height: 540 }; const summary_box = { width: 405, height: 540 };
const main_title = { align: "center", width: 1196, height: 883, x: 586, y: 99}; const main_title = { align: "center", width: 1196, height: 883, x: 586, y: 99};
const main_box = { width: 928, height: 535, x: 720, y: 323}; const main_box = { width: 928, height: 535, x: 720, y: 323};
const FORCE = false;
export const description = "Generates a PDF presentation from an input .md file and a background template." export const description = "Generates a PDF presentation from an input .md file and a background template."
@ -22,23 +21,16 @@ export const description = "Generates a PDF presentation from an input .md file
export const options = [ export const options = [
["--heading-font <path>", "Font file to use for headings.", "static/fonts/VictorMono-Bold.ttf"], ["--heading-font <path>", "Font file to use for headings.", "static/fonts/VictorMono-Bold.ttf"],
["--body-font <path>", "Font file to use for body text.", "static/fonts/VictorMono-Medium.ttf"], ["--body-font <path>", "Font file to use for body text.", "static/fonts/VictorMono-Medium.ttf"],
["--output <path>", "The output .pdf file to write. Defaults to <input>.pdf"], ["--no-exit", "Normally only used when called directly from other commands."],
["--force", "Force the generation no matter what.", FORCE]
] ]
// put required options in the required variable // put required options in the required variable
export const required = [ export const required = [
["--input <path>", "The input .md file for the generated presentation."], ["--input <path>", "The input .md file _GLOB_ for the source files."],
["--template <path>", "The background template image to use."], ["--template <path>", "The background template image to use."],
] ]
// handy function for checking things are good and aborting
const check = (test, fail_message) => {
if(!test) {
log.error(fail_message);
process.exit(1);
}
}
export const start_pdf = (opts) => { export const start_pdf = (opts) => {
const doc = new PDFDocument({ const doc = new PDFDocument({
font: "Courier", font: "Courier",
@ -60,6 +52,16 @@ export const start_pdf = (opts) => {
return [doc, out_stream]; return [doc, out_stream];
} }
const valign = (doc, text, options) => {
assert(options.y !== undefined, "Missing y in options");
assert(options.height !== undefined, "Missing height in options");
const h = doc.heightOfString(text, options);
const start = options.y + (0.5 * (options.height - h));
return start;
}
const write_page = (doc, template, content) => { const write_page = (doc, template, content) => {
// place the background image // place the background image
doc.image(template, 0, 0, { doc.image(template, 0, 0, {
@ -100,19 +102,43 @@ const write_page = (doc, template, content) => {
} else if(content.type == "title-only") { } else if(content.type == "title-only") {
// it's a title only slide // it's a title only slide
doc.fontSize(160); doc.fontSize(160);
doc.font("heading").text(content.slide_title, main_title.x, main_title.y + (main_title.height / 3.5), main_title);
const title_start = valign(doc, content.slide_title, main_title);
doc.font("heading").text(content.slide_title, main_title.x, title_start, main_title);
} else { } else {
// this handles title-text slides, and anything else // this handles title-text slides, and anything else
// if I put a # I want this to be a subtitle
doc.fontSize(120); doc.fontSize(120);
// add the title/body text doc.font("heading");
doc.font("heading").text(content.slide_title, main_title.x, main_title.y, main_title);
// if I put a # I want this to be a subtitle
if(content.slide_body.startsWith("#")) { if(content.slide_body.startsWith("#")) {
// calculate the title height
let title_start = valign(doc, content.slide_title, main_title);
// calculate 1 line for the move down
doc.fontSize(100);
doc.font("body");
title_start -= doc.heightOfString("H", main_title);
doc.fontSize(120);
// add the title/body text
doc.font("heading").text(content.slide_title, main_title.x, title_start, main_title);
doc.fontSize(100); doc.fontSize(100);
const trim_body = content.slide_body.slice(1).trim(); const trim_body = content.slide_body.slice(1).trim();
doc.font("body").text(trim_body, main_box.x, main_box.y, {align: "center", ...main_box}); doc.moveDown(1);
doc.font("body").text(trim_body, {
align: "center",
});
} else { } else {
doc.fontSize(120);
// add the title/body text
doc.font("heading").text(content.slide_title, main_title.x, main_title.y, main_title);
doc.moveDown(1);
doc.fontSize(60); doc.fontSize(60);
doc.font("body").text(content.slide_body, main_box.x, main_box.y, main_box); doc.font("body").text(content.slide_body, main_box.x, main_box.y, main_box);
} }
@ -168,16 +194,13 @@ const parse_input = (input) => {
const make_pdf_path = (input) => { const make_pdf_path = (input) => {
const result = path.parse(input); const result = path.parse(input);
const target = path.join(result.dir, `${result.name}.pdf`);
return path.join(result.dir, `${result.name}.pdf`); mkdir_to(target);
return target;
} }
const generate_presentation = (opts, waiting) => { const generate_presentation = (opts, waiting) => {
try { try {
if(!opts.output) {
opts.output = make_pdf_path(opts.input);
}
const [doc, out_stream] = start_pdf(opts); const [doc, out_stream] = start_pdf(opts);
out_stream.on("finish", () => waiting.resolve()); out_stream.on("finish", () => waiting.resolve());
@ -203,16 +226,21 @@ const generate_presentation = (opts, waiting) => {
} }
export const main = async (opts) => { export const main = async (opts) => {
const in_files = glob.sync(opts.input); const in_files = glob(opts.input);
for(let file of in_files) { for(let file of in_files) {
console.log(file);
const waiting = defer(file);
const settings = {...opts}; const settings = {...opts};
settings.input = file; settings.input = file;
generate_presentation(settings, waiting); settings.output = make_pdf_path(settings.input);
await waiting;
if(opts.force || changed(settings.input, settings.output)) {
const waiting = defer(file);
generate_presentation(settings, waiting);
await waiting;
} else {
console.log("SKIP", settings.input);
}
} }
process.exit(0); if(!opts.noExit) process.exit(0);
} }

@ -1,3 +1,4 @@
/* __FOOTGUN__ Don't put anything secret in here. */
export const company = { export const company = {
owner: 'Cool Coder', owner: 'Cool Coder',
product: 'Super Product', product: 'Super Product',

Loading…
Cancel
Save