mirror of
https://github.com/NohamR/pdfy.git
synced 2026-10-10 18:29:55 +00:00
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:
@@ -30,7 +30,7 @@ node src/index.js --help
|
|||||||
Usage: pdfy [options] <url>
|
Usage: pdfy [options] <url>
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
url URL of the article to convert
|
url URL of the article to convert, or path to a local HTML file
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-t, --theme <theme> Reader View theme (light, dark, sepia, groove-dark, solarized-light, solarized-dark, nord-light, nord-dark)
|
-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
|
```bash
|
||||||
node src/index.js https://example.com/article
|
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 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 --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 --highlight-style monokai
|
||||||
node src/index.js https://example.com/article --config .pdfyrc
|
node src/index.js https://example.com/article --config .pdfyrc
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const program = new Command()
|
|||||||
program
|
program
|
||||||
.name('pdfy')
|
.name('pdfy')
|
||||||
.description('Convert web articles to PDF using Reader View and Puppeteer')
|
.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(
|
.option(
|
||||||
'-t, --theme <theme>',
|
'-t, --theme <theme>',
|
||||||
`Reader View theme (${VALID_THEMES.join(', ')})`
|
`Reader View theme (${VALID_THEMES.join(', ')})`
|
||||||
@@ -42,6 +42,8 @@ program
|
|||||||
Examples:
|
Examples:
|
||||||
node src/index.js <URL>
|
node src/index.js <URL>
|
||||||
node src/index.js <URL> --theme dark --highlight-style monokai --css config/rules.css
|
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
|
npm start -- <URL> --theme dark --highlight-style monokai --css config/rules.css
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|||||||
11
src/index.js
11
src/index.js
@@ -1,6 +1,7 @@
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import process from 'node:process'
|
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
import { pathToFileURL } from 'node:url'
|
||||||
|
import process from 'node:process'
|
||||||
import readline from 'node:readline/promises'
|
import readline from 'node:readline/promises'
|
||||||
import { parseArgs } from './cli.js'
|
import { parseArgs } from './cli.js'
|
||||||
import { loadConfigFile, loadPreferences, readCustomCss } from './config.js'
|
import { loadConfigFile, loadPreferences, readCustomCss } from './config.js'
|
||||||
@@ -31,7 +32,12 @@ async function main () {
|
|||||||
const customCss = cssPath ? readCustomCss(path.resolve(cssPath)) : null
|
const customCss = cssPath ? readCustomCss(path.resolve(cssPath)) : null
|
||||||
|
|
||||||
let html
|
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++) {
|
for (let attempt = 0; attempt < 2; attempt++) {
|
||||||
try {
|
try {
|
||||||
@@ -66,6 +72,7 @@ async function main () {
|
|||||||
|
|
||||||
logger.info(`Loading article from local file: "${resolvedPath}"`)
|
logger.info(`Loading article from local file: "${resolvedPath}"`)
|
||||||
html = fs.readFileSync(resolvedPath, 'utf-8')
|
html = fs.readFileSync(resolvedPath, 'utf-8')
|
||||||
|
currentUrl = pathToFileURL(resolvedPath).href
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
throw err
|
throw err
|
||||||
|
|||||||
17
src/pdf.js
17
src/pdf.js
@@ -1,5 +1,6 @@
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
import { pathToFileURL } from 'node:url'
|
||||||
import logger from './logger.js'
|
import logger from './logger.js'
|
||||||
|
|
||||||
const HIGHLIGHT_JS =
|
const HIGHLIGHT_JS =
|
||||||
@@ -44,8 +45,19 @@ export function enhanceHtml (html, customCss, highlightStyle = 'github') {
|
|||||||
return enhanced
|
return enhanced
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generatePdf (page, html, outputPath, { timeout = 30000 } = {}) {
|
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.setContent(html, { waitUntil: 'networkidle0', timeout })
|
||||||
|
}
|
||||||
|
|
||||||
await page.waitForFunction(
|
await page.waitForFunction(
|
||||||
() => Array.from(document.images).every(img => img.complete),
|
() => Array.from(document.images).every(img => img.complete),
|
||||||
{ timeout, polling: 200 }
|
{ timeout, polling: 200 }
|
||||||
@@ -68,5 +80,8 @@ export async function generatePdf (page, html, outputPath, { timeout = 30000 } =
|
|||||||
if (outputPath) {
|
if (outputPath) {
|
||||||
logger.info(`PDF saved: "${outputPath}"`)
|
logger.info(`PDF saved: "${outputPath}"`)
|
||||||
}
|
}
|
||||||
|
if (tempFile) {
|
||||||
|
try { fs.unlinkSync(tempFile) } catch { }
|
||||||
|
}
|
||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export async function convert (url, options = {}) {
|
|||||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true })
|
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()
|
await pdfPage.close()
|
||||||
|
|
||||||
return { title, pdfBuffer, outputPath }
|
return { title, pdfBuffer, outputPath }
|
||||||
|
|||||||
Reference in New Issue
Block a user