💄 show novel title in single chapter export PDF

This commit is contained in:
2026-05-27 18:34:20 +10:00
parent 1b9275fc97
commit 26c4e8a90c
2 changed files with 29 additions and 9 deletions

View File

@@ -192,13 +192,14 @@ export function registerIpcHandlers(): void {
if (result.canceled || !result.filePath) return if (result.canceled || !result.filePath) return
const { marked } = await import('marked') const { marked } = await import('marked')
const { PDFDocument, rgb, StandardFonts } = await import('pdf-lib')
// Draft files use single \n between paragraphs; marked needs \n\n to create <p> elements // Draft files use single \n between paragraphs; marked needs \n\n to create <p> elements
const normalized = content.replace(/\r\n/g, '\n').replace(/\n(?!\n)/g, '\n\n') const normalized = content.replace(/\r\n/g, '\n').replace(/\n(?!\n)/g, '\n\n')
const bodyHtml = await marked(normalized) const bodyHtml = await marked(normalized)
// Escape for safe injection into HTML attributes
const safeTitle = fileName.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;') const safeTitle = fileName.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;')
const headerTitle = fileName.toUpperCase().replace(/</g, '&lt;').replace(/>/g, '&gt;') const projectTitle = getProjectTitle().toUpperCase()
const chapterTitle = fileName.toUpperCase()
const html = `<!DOCTYPE html> const html = `<!DOCTYPE html>
<html> <html>
@@ -206,7 +207,7 @@ export function registerIpcHandlers(): void {
<meta charset="utf-8"> <meta charset="utf-8">
<title>${safeTitle}</title> <title>${safeTitle}</title>
<style> <style>
@page { size: letter; margin: 1in; } @page { size: letter; }
body { body {
font-family: "Courier New", Courier, monospace; font-family: "Courier New", Courier, monospace;
font-size: 12pt; font-size: 12pt;
@@ -264,12 +265,31 @@ export function registerIpcHandlers(): void {
await offscreen.loadFile(tempPath) await offscreen.loadFile(tempPath)
const pdfBuffer = await offscreen.webContents.printToPDF({ const pdfBuffer = await offscreen.webContents.printToPDF({
pageSize: 'Letter', pageSize: 'Letter',
displayHeaderFooter: true, displayHeaderFooter: false,
headerTemplate: `<div style="font-size:11pt;font-family:'Courier New',Courier,monospace;width:100%;text-align:right;padding-right:72pt;">${headerTitle} / <span class="pageNumber"></span></div>`, margins: { marginType: 'custom', top: 1.0, bottom: 1.0, left: 1.0, right: 1.0 }
footerTemplate: '<div></div>',
margins: { marginType: 'custom', top: 1.25, bottom: 1.0, left: 1.0, right: 1.0 }
}) })
writeFileSync(result.filePath, pdfBuffer)
// Stamp the running header on every page using pdf-lib
const pdfDoc = await PDFDocument.load(pdfBuffer)
const courier = await pdfDoc.embedFont(StandardFonts.Courier)
const pages = pdfDoc.getPages()
for (let i = 0; i < pages.length; i++) {
const page = pages[i]
const { width, height } = page.getSize()
const headerText = `${projectTitle} / ${chapterTitle} / ${i + 1}`
const fontSize = 11
const textWidth = courier.widthOfTextAtSize(headerText, fontSize)
page.drawText(headerText, {
x: width - 72 - textWidth, // 1 in from right edge
y: height - 36, // 0.5 in from top edge
size: fontSize,
font: courier,
color: rgb(0, 0, 0)
})
}
const pdfBytes = await pdfDoc.save()
writeFileSync(result.filePath, pdfBytes)
} finally { } finally {
offscreen.destroy() offscreen.destroy()
try { unlinkSync(tempPath) } catch { /* ignore */ } try { unlinkSync(tempPath) } catch { /* ignore */ }

File diff suppressed because one or more lines are too long