From ff85501af390a26575af12408d4ef3d5066998e1 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 9 Jun 2023 16:06:11 -0400 Subject: [PATCH] pdf generation command needs an update --- commands/pdfpresgen.js | 90 +++++++++++++++++++---------- commands/templates/emails/config.js | 1 + 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/commands/pdfpresgen.js b/commands/pdfpresgen.js index 5684d04..1edb7cd 100644 --- a/commands/pdfpresgen.js +++ b/commands/pdfpresgen.js @@ -1,12 +1,11 @@ // you may not need all of these but they come up a lot import fs from "fs"; -import assert from "assert"; import logging from '../lib/logging.js'; -import glob from "fast-glob"; import path from "path"; -import template from "lodash/template.js"; import { defer } from "../lib/api.js"; import PDFDocument from "pdfkit"; +import assert from "assert"; +import { mkdir_to, glob, changed } from "../lib/builderator.js"; 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 main_title = { align: "center", width: 1196, height: 883, x: 586, y: 99}; 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." @@ -22,23 +21,16 @@ export const description = "Generates a PDF presentation from an input .md file export const options = [ ["--heading-font ", "Font file to use for headings.", "static/fonts/VictorMono-Bold.ttf"], ["--body-font ", "Font file to use for body text.", "static/fonts/VictorMono-Medium.ttf"], - ["--output ", "The output .pdf file to write. Defaults to .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 export const required = [ - ["--input ", "The input .md file for the generated presentation."], + ["--input ", "The input .md file _GLOB_ for the source files."], ["--template ", "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) => { const doc = new PDFDocument({ font: "Courier", @@ -60,6 +52,16 @@ export const start_pdf = (opts) => { 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) => { // place the background image doc.image(template, 0, 0, { @@ -100,19 +102,43 @@ const write_page = (doc, template, content) => { } else if(content.type == "title-only") { // it's a title only slide 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 { // this handles title-text slides, and anything else + // if I put a # I want this to be a subtitle doc.fontSize(120); - // add the title/body text - doc.font("heading").text(content.slide_title, main_title.x, main_title.y, main_title); + doc.font("heading"); - // if I put a # I want this to be a subtitle 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); 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 { + 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.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 result = path.parse(input); - - return path.join(result.dir, `${result.name}.pdf`); + const target = path.join(result.dir, `${result.name}.pdf`); + mkdir_to(target); + return target; } const generate_presentation = (opts, waiting) => { try { - if(!opts.output) { - opts.output = make_pdf_path(opts.input); - } - const [doc, out_stream] = start_pdf(opts); out_stream.on("finish", () => waiting.resolve()); @@ -203,16 +226,21 @@ const generate_presentation = (opts, waiting) => { } export const main = async (opts) => { - const in_files = glob.sync(opts.input); + const in_files = glob(opts.input); for(let file of in_files) { - console.log(file); - const waiting = defer(file); const settings = {...opts}; settings.input = file; - generate_presentation(settings, waiting); - await waiting; + settings.output = make_pdf_path(settings.input); + + 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); } diff --git a/commands/templates/emails/config.js b/commands/templates/emails/config.js index f3c929d..c4f15f2 100644 --- a/commands/templates/emails/config.js +++ b/commands/templates/emails/config.js @@ -1,3 +1,4 @@ +/* __FOOTGUN__ Don't put anything secret in here. */ export const company = { owner: 'Cool Coder', product: 'Super Product',