Support local HTML files for conversion

Allow the CLI argument to be either a remote URL or a local HTML path, and document the new usage in README/help examples.
This commit is contained in:
√(noham)²
2026-07-24 23:05:25 +02:00
parent 9711bc3dc1
commit fd941700d7
5 changed files with 33 additions and 7 deletions

View File

@@ -30,7 +30,7 @@ node src/index.js --help
Usage: pdfy [options] <url>
Arguments:
url URL of the article to convert
url URL of the article to convert, or path to a local HTML file
Options:
-t, --theme <theme> Reader View theme (light, dark, sepia, groove-dark, solarized-light, solarized-dark, nord-light, nord-dark)
@@ -77,7 +77,9 @@ CLI flags always override config file values. Use `--config <path>` to specify a
```bash
node src/index.js https://example.com/article
node src/index.js /path/to/saved/page.html
node src/index.js https://example.com/article --theme dark --css config/rules.css
node src/index.js /path/to/saved/page.html --theme dark --highlight-style monokai
node src/index.js https://example.com/article --prefs config/reader-view-preferences.json
node src/index.js https://example.com/article --highlight-style monokai
node src/index.js https://example.com/article --config .pdfyrc

View File

@@ -16,7 +16,7 @@ const program = new Command()
program
.name('pdfy')
.description('Convert web articles to PDF using Reader View and Puppeteer')
.argument('<url>', 'URL of the article to convert')
.argument('<url>', 'URL of the article to convert, or path to a local HTML file')
.option(
'-t, --theme <theme>',
`Reader View theme (${VALID_THEMES.join(', ')})`
@@ -42,6 +42,8 @@ program
Examples:
node src/index.js <URL>
node src/index.js <URL> --theme dark --highlight-style monokai --css config/rules.css
node src/index.js <path/to/file.html> --theme dark --highlight-style monokai
node src/index.js <URL> --prefs config/reader-view-preferences.json
npm start -- <URL> --theme dark --highlight-style monokai --css config/rules.css
`
)

View File

@@ -1,6 +1,7 @@
import fs from 'node:fs'
import process from 'node:process'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
import process from 'node:process'
import readline from 'node:readline/promises'
import { parseArgs } from './cli.js'
import { loadConfigFile, loadPreferences, readCustomCss } from './config.js'
@@ -31,7 +32,12 @@ async function main () {
const customCss = cssPath ? readCustomCss(path.resolve(cssPath)) : null
let html
const currentUrl = url
const filePath = path.resolve(url)
let currentUrl = url
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
currentUrl = pathToFileURL(filePath).href
logger.info(`Detected local file, converting to file URL: ${currentUrl}`)
}
for (let attempt = 0; attempt < 2; attempt++) {
try {
@@ -66,6 +72,7 @@ async function main () {
logger.info(`Loading article from local file: "${resolvedPath}"`)
html = fs.readFileSync(resolvedPath, 'utf-8')
currentUrl = pathToFileURL(resolvedPath).href
continue
}
throw err

View File

@@ -1,5 +1,6 @@
import fs from 'node:fs'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
import logger from './logger.js'
const HIGHLIGHT_JS =
@@ -44,8 +45,19 @@ export function enhanceHtml (html, customCss, highlightStyle = 'github') {
return enhanced
}
export async function generatePdf (page, html, outputPath, { timeout = 30000 } = {}) {
await page.setContent(html, { waitUntil: 'networkidle0', timeout })
export async function generatePdf (page, html, outputPath, { timeout = 30000, url = null } = {}) {
const isFileUrl = url && url.startsWith('file://')
let tempFile = null
if (isFileUrl) {
const sourceDir = path.dirname(new URL(url).pathname)
tempFile = path.join(sourceDir, `.pdfy-temp-${Date.now()}.html`)
fs.writeFileSync(tempFile, html)
await page.goto(pathToFileURL(tempFile).href, { waitUntil: 'networkidle0', timeout })
} else {
await page.setContent(html, { waitUntil: 'networkidle0', timeout })
}
await page.waitForFunction(
() => Array.from(document.images).every(img => img.complete),
{ timeout, polling: 200 }
@@ -68,5 +80,8 @@ export async function generatePdf (page, html, outputPath, { timeout = 30000 } =
if (outputPath) {
logger.info(`PDF saved: "${outputPath}"`)
}
if (tempFile) {
try { fs.unlinkSync(tempFile) } catch { }
}
return buffer
}

View File

@@ -124,7 +124,7 @@ export async function convert (url, options = {}) {
fs.mkdirSync(path.dirname(outputPath), { recursive: true })
}
const pdfBuffer = await generatePdf(pdfPage, enhancedHtml, outputPath, { timeout })
const pdfBuffer = await generatePdf(pdfPage, enhancedHtml, outputPath, { timeout, url })
await pdfPage.close()
return { title, pdfBuffer, outputPath }