screenitshot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +63 -0
- package/dist/cli.js.map +1 -0
- package/dist/detector.d.ts +3 -0
- package/dist/detector.d.ts.map +1 -0
- package/dist/detector.js +28 -0
- package/dist/detector.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/renderer.d.ts +7 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +82 -0
- package/dist/renderer.js.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/eslint.config.js +40 -0
- package/package.json +45 -0
- package/src/cli.ts +70 -0
- package/src/detector.ts +31 -0
- package/src/index.ts +19 -0
- package/src/renderer.ts +113 -0
- package/src/types.ts +24 -0
- package/tsconfig.json +20 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { screenshot } from './index.js';
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
import { resolve, dirname } from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
const packageJson = JSON.parse(await readFile(resolve(__dirname, '../package.json'), 'utf-8'));
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('screenitshot')
|
|
13
|
+
.description('Convert various file formats to high-quality screenshots')
|
|
14
|
+
.version(packageJson.version)
|
|
15
|
+
.argument('<input>', 'Input file path')
|
|
16
|
+
.argument('[output]', 'Output image path')
|
|
17
|
+
.option('-f, --format <format>', 'Output image format (png, jpeg, webp)', 'png')
|
|
18
|
+
.option('-w, --width <width>', 'Viewport width', '1920')
|
|
19
|
+
.option('-h, --height <height>', 'Viewport height', '1080')
|
|
20
|
+
.option('-p, --page <page>', 'Page number for multi-page documents', '1')
|
|
21
|
+
.action(async (input, output, options) => {
|
|
22
|
+
try {
|
|
23
|
+
console.log(`Converting ${input}...`);
|
|
24
|
+
const result = await screenshot(input, {
|
|
25
|
+
output,
|
|
26
|
+
format: options.format,
|
|
27
|
+
width: parseInt(options.width),
|
|
28
|
+
height: parseInt(options.height),
|
|
29
|
+
page: parseInt(options.page),
|
|
30
|
+
});
|
|
31
|
+
console.log(`✓ Screenshot saved to ${result.path}`);
|
|
32
|
+
console.log(` Format: ${result.format}`);
|
|
33
|
+
console.log(` Size: ${result.width}x${result.height}`);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const err = error;
|
|
37
|
+
// User-friendly error messages
|
|
38
|
+
if (err.message.includes('ENOENT') || err.message.includes('no such file')) {
|
|
39
|
+
console.error(`Error: Input file not found: ${input}`);
|
|
40
|
+
}
|
|
41
|
+
else if (err.message.includes('Unsupported file format')) {
|
|
42
|
+
console.error(`Error: ${err.message}`);
|
|
43
|
+
console.error('Supported formats: PDF');
|
|
44
|
+
}
|
|
45
|
+
else if (err.message.includes('No template available')) {
|
|
46
|
+
console.error(`Error: Format not yet supported`);
|
|
47
|
+
console.error('Currently supported: PDF');
|
|
48
|
+
}
|
|
49
|
+
else if (err.message.includes('page')) {
|
|
50
|
+
console.error(`Error: Invalid page number or page not found`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.error(`Error: ${err.message}`);
|
|
54
|
+
// Show stack trace in debug mode
|
|
55
|
+
if (process.env.DEBUG) {
|
|
56
|
+
console.error(err.stack);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
program.parse();
|
|
63
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA0BxE"}
|
package/dist/detector.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { extname } from 'path';
|
|
3
|
+
export async function detectFormat(filePath) {
|
|
4
|
+
const ext = extname(filePath).toLowerCase();
|
|
5
|
+
// Basic extension-based detection
|
|
6
|
+
const extensionMap = {
|
|
7
|
+
'.pdf': 'pdf',
|
|
8
|
+
'.epub': 'epub',
|
|
9
|
+
'.docx': 'docx',
|
|
10
|
+
};
|
|
11
|
+
if (ext in extensionMap) {
|
|
12
|
+
return extensionMap[ext];
|
|
13
|
+
}
|
|
14
|
+
// Fallback: check magic bytes
|
|
15
|
+
try {
|
|
16
|
+
const buffer = await readFile(filePath);
|
|
17
|
+
const magic = buffer.slice(0, 4).toString('hex');
|
|
18
|
+
if (magic === '25504446')
|
|
19
|
+
return 'pdf'; // %PDF
|
|
20
|
+
if (buffer.slice(0, 2).toString() === 'PK')
|
|
21
|
+
return 'epub'; // ZIP-based
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Ignore read errors
|
|
25
|
+
}
|
|
26
|
+
return 'unknown';
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAG/B,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAE5C,kCAAkC;IAClC,MAAM,YAAY,GAA+B;QAC/C,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,MAAM;KAChB,CAAC;IAEF,IAAI,GAAG,IAAI,YAAY,EAAE,CAAC;QACxB,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,8BAA8B;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAEjD,IAAI,KAAK,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC,CAAE,OAAO;QAChD,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC,CAAE,YAAY;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,qBAAqB;IACvB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScreenshotOptions, ScreenshotResult } from './types.js';
|
|
2
|
+
export { type ScreenshotOptions, type ScreenshotResult, type FileFormat } from './types.js';
|
|
3
|
+
export declare function screenshot(inputPath: string, options?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5F,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAS3B"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { detectFormat } from './detector.js';
|
|
2
|
+
import { Renderer } from './renderer.js';
|
|
3
|
+
export async function screenshot(inputPath, options = {}) {
|
|
4
|
+
const format = await detectFormat(inputPath);
|
|
5
|
+
if (format === 'unknown') {
|
|
6
|
+
throw new Error(`Unsupported file format: ${inputPath}`);
|
|
7
|
+
}
|
|
8
|
+
const renderer = new Renderer();
|
|
9
|
+
return await renderer.render(inputPath, format, options);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAKzC,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,SAAiB,EACjB,UAA6B,EAAE;IAE/B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;IAE7C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,4BAA4B,SAAS,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FileFormat, ScreenshotOptions, ScreenshotResult } from './types.js';
|
|
2
|
+
export declare class Renderer {
|
|
3
|
+
private getTemplatePath;
|
|
4
|
+
private injectDataIntoPage;
|
|
5
|
+
render(inputPath: string, format: FileFormat, options?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKlF,qBAAa,QAAQ;IACnB,OAAO,CAAC,eAAe;YAeT,kBAAkB;IAa1B,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC;CAsE7B"}
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { resolve, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
export class Renderer {
|
|
8
|
+
getTemplatePath(format) {
|
|
9
|
+
const templateMap = {
|
|
10
|
+
pdf: resolve(__dirname, '../templates/pdf.html'),
|
|
11
|
+
epub: resolve(__dirname, '../templates/epub.html'),
|
|
12
|
+
docx: resolve(__dirname, '../templates/docx.html'),
|
|
13
|
+
unknown: '',
|
|
14
|
+
};
|
|
15
|
+
const path = templateMap[format];
|
|
16
|
+
if (!path) {
|
|
17
|
+
throw new Error(`No template available for format: ${format}`);
|
|
18
|
+
}
|
|
19
|
+
return path;
|
|
20
|
+
}
|
|
21
|
+
async injectDataIntoPage(page, fileBase64, pageNumber = 1) {
|
|
22
|
+
// Inject data into page globals before template loads
|
|
23
|
+
await page.addInitScript(({ fileBase64: fb64, pageNum }) => {
|
|
24
|
+
// Override the placeholder values
|
|
25
|
+
globalThis.fileBase64 = fb64;
|
|
26
|
+
globalThis.pageNumber = pageNum;
|
|
27
|
+
}, { fileBase64, pageNum: pageNumber });
|
|
28
|
+
}
|
|
29
|
+
async render(inputPath, format, options = {}) {
|
|
30
|
+
const { output, format: imageFormat = 'png', width = 1920, height = 1080, page: pageNumber = 1, } = options;
|
|
31
|
+
const outputPath = output || inputPath.replace(/\.[^.]+$/, `.${imageFormat}`);
|
|
32
|
+
// Launch browser
|
|
33
|
+
const browser = await chromium.launch({
|
|
34
|
+
headless: true,
|
|
35
|
+
});
|
|
36
|
+
try {
|
|
37
|
+
const page = await browser.newPage({
|
|
38
|
+
viewport: { width, height },
|
|
39
|
+
});
|
|
40
|
+
// Read and encode file as base64
|
|
41
|
+
const fileData = await readFile(inputPath);
|
|
42
|
+
const fileBase64 = fileData.toString('base64');
|
|
43
|
+
// Inject data before loading template
|
|
44
|
+
await this.injectDataIntoPage(page, fileBase64, pageNumber);
|
|
45
|
+
// Load template
|
|
46
|
+
const templatePath = this.getTemplatePath(format);
|
|
47
|
+
await page.goto(`file://${templatePath}`);
|
|
48
|
+
// Wait for render complete and get metadata
|
|
49
|
+
const metadata = await page.evaluate(async () => {
|
|
50
|
+
const renderComplete = globalThis.renderComplete;
|
|
51
|
+
if (!renderComplete) {
|
|
52
|
+
throw new Error('window.renderComplete not found');
|
|
53
|
+
}
|
|
54
|
+
// Await the promise to get metadata
|
|
55
|
+
return await renderComplete;
|
|
56
|
+
});
|
|
57
|
+
// Resize viewport to match actual rendered content
|
|
58
|
+
await page.setViewportSize({
|
|
59
|
+
width: metadata.width,
|
|
60
|
+
height: metadata.height,
|
|
61
|
+
});
|
|
62
|
+
// Take screenshot at exact rendered size
|
|
63
|
+
await page.screenshot({
|
|
64
|
+
path: outputPath,
|
|
65
|
+
type: imageFormat,
|
|
66
|
+
fullPage: false,
|
|
67
|
+
});
|
|
68
|
+
await browser.close();
|
|
69
|
+
return {
|
|
70
|
+
path: outputPath,
|
|
71
|
+
format: imageFormat,
|
|
72
|
+
width: metadata.width,
|
|
73
|
+
height: metadata.height,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
await browser.close();
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ScreenshotOptions {
|
|
2
|
+
output?: string;
|
|
3
|
+
format?: 'png' | 'jpeg' | 'webp';
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
page?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ScreenshotResult {
|
|
9
|
+
path: string;
|
|
10
|
+
format: string;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RenderMetadata {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
pageCount: number;
|
|
18
|
+
pageNumber: number;
|
|
19
|
+
scale: number;
|
|
20
|
+
}
|
|
21
|
+
export type FileFormat = 'pdf' | 'epub' | 'docx' | 'unknown';
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import tseslint from '@typescript-eslint/eslint-plugin';
|
|
3
|
+
import tsparser from '@typescript-eslint/parser';
|
|
4
|
+
|
|
5
|
+
export default [
|
|
6
|
+
eslint.configs.recommended,
|
|
7
|
+
{
|
|
8
|
+
files: ['**/*.ts'],
|
|
9
|
+
languageOptions: {
|
|
10
|
+
parser: tsparser,
|
|
11
|
+
parserOptions: {
|
|
12
|
+
ecmaVersion: 'latest',
|
|
13
|
+
sourceType: 'module',
|
|
14
|
+
},
|
|
15
|
+
globals: {
|
|
16
|
+
console: 'readonly',
|
|
17
|
+
process: 'readonly',
|
|
18
|
+
__dirname: 'readonly',
|
|
19
|
+
__filename: 'readonly',
|
|
20
|
+
Buffer: 'readonly',
|
|
21
|
+
global: 'readonly',
|
|
22
|
+
setTimeout: 'readonly',
|
|
23
|
+
clearTimeout: 'readonly',
|
|
24
|
+
setInterval: 'readonly',
|
|
25
|
+
clearInterval: 'readonly',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
plugins: {
|
|
29
|
+
'@typescript-eslint': tseslint,
|
|
30
|
+
},
|
|
31
|
+
rules: {
|
|
32
|
+
...tseslint.configs.recommended.rules,
|
|
33
|
+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
34
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
ignores: ['node_modules/**', 'dist/**', 'templates/**'],
|
|
39
|
+
},
|
|
40
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "screenitshot",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Convert various file formats to high-quality screenshots",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"screenitshot": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && npm run copy-templates",
|
|
13
|
+
"copy-templates": "mkdir -p templates && cp ../render/dist/pdf.html templates/",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
16
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"screenshot",
|
|
21
|
+
"pdf",
|
|
22
|
+
"converter",
|
|
23
|
+
"playwright",
|
|
24
|
+
"chromium"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"playwright": "^1.40.1",
|
|
30
|
+
"commander": "^11.1.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.10.6",
|
|
34
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
35
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
36
|
+
"eslint": "^8.56.0",
|
|
37
|
+
"typescript": "^5.3.3"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { screenshot } from './index.js';
|
|
5
|
+
import { readFile } from 'fs/promises';
|
|
6
|
+
import { resolve, dirname } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
|
|
12
|
+
const packageJson = JSON.parse(
|
|
13
|
+
await readFile(resolve(__dirname, '../package.json'), 'utf-8')
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.name('screenitshot')
|
|
20
|
+
.description('Convert various file formats to high-quality screenshots')
|
|
21
|
+
.version(packageJson.version)
|
|
22
|
+
.argument('<input>', 'Input file path')
|
|
23
|
+
.argument('[output]', 'Output image path')
|
|
24
|
+
.option('-f, --format <format>', 'Output image format (png, jpeg, webp)', 'png')
|
|
25
|
+
.option('-w, --width <width>', 'Viewport width', '1920')
|
|
26
|
+
.option('-h, --height <height>', 'Viewport height', '1080')
|
|
27
|
+
.option('-p, --page <page>', 'Page number for multi-page documents', '1')
|
|
28
|
+
.action(async (input: string, output: string | undefined, options: any) => {
|
|
29
|
+
try {
|
|
30
|
+
console.log(`Converting ${input}...`);
|
|
31
|
+
|
|
32
|
+
const result = await screenshot(input, {
|
|
33
|
+
output,
|
|
34
|
+
format: options.format,
|
|
35
|
+
width: parseInt(options.width),
|
|
36
|
+
height: parseInt(options.height),
|
|
37
|
+
page: parseInt(options.page),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log(`✓ Screenshot saved to ${result.path}`);
|
|
41
|
+
console.log(` Format: ${result.format}`);
|
|
42
|
+
console.log(` Size: ${result.width}x${result.height}`);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
const err = error as Error;
|
|
45
|
+
|
|
46
|
+
// User-friendly error messages
|
|
47
|
+
if (err.message.includes('ENOENT') || err.message.includes('no such file')) {
|
|
48
|
+
console.error(`Error: Input file not found: ${input}`);
|
|
49
|
+
} else if (err.message.includes('Unsupported file format')) {
|
|
50
|
+
console.error(`Error: ${err.message}`);
|
|
51
|
+
console.error('Supported formats: PDF');
|
|
52
|
+
} else if (err.message.includes('No template available')) {
|
|
53
|
+
console.error(`Error: Format not yet supported`);
|
|
54
|
+
console.error('Currently supported: PDF');
|
|
55
|
+
} else if (err.message.includes('page')) {
|
|
56
|
+
console.error(`Error: Invalid page number or page not found`);
|
|
57
|
+
} else {
|
|
58
|
+
console.error(`Error: ${err.message}`);
|
|
59
|
+
|
|
60
|
+
// Show stack trace in debug mode
|
|
61
|
+
if (process.env.DEBUG) {
|
|
62
|
+
console.error(err.stack);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
program.parse();
|
package/src/detector.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { extname } from 'path';
|
|
3
|
+
import type { FileFormat } from './types.js';
|
|
4
|
+
|
|
5
|
+
export async function detectFormat(filePath: string): Promise<FileFormat> {
|
|
6
|
+
const ext = extname(filePath).toLowerCase();
|
|
7
|
+
|
|
8
|
+
// Basic extension-based detection
|
|
9
|
+
const extensionMap: Record<string, FileFormat> = {
|
|
10
|
+
'.pdf': 'pdf',
|
|
11
|
+
'.epub': 'epub',
|
|
12
|
+
'.docx': 'docx',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
if (ext in extensionMap) {
|
|
16
|
+
return extensionMap[ext];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Fallback: check magic bytes
|
|
20
|
+
try {
|
|
21
|
+
const buffer = await readFile(filePath);
|
|
22
|
+
const magic = buffer.slice(0, 4).toString('hex');
|
|
23
|
+
|
|
24
|
+
if (magic === '25504446') return 'pdf'; // %PDF
|
|
25
|
+
if (buffer.slice(0, 2).toString() === 'PK') return 'epub'; // ZIP-based
|
|
26
|
+
} catch {
|
|
27
|
+
// Ignore read errors
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return 'unknown';
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { detectFormat } from './detector.js';
|
|
2
|
+
import { Renderer } from './renderer.js';
|
|
3
|
+
import type { ScreenshotOptions, ScreenshotResult } from './types.js';
|
|
4
|
+
|
|
5
|
+
export { type ScreenshotOptions, type ScreenshotResult, type FileFormat } from './types.js';
|
|
6
|
+
|
|
7
|
+
export async function screenshot(
|
|
8
|
+
inputPath: string,
|
|
9
|
+
options: ScreenshotOptions = {}
|
|
10
|
+
): Promise<ScreenshotResult> {
|
|
11
|
+
const format = await detectFormat(inputPath);
|
|
12
|
+
|
|
13
|
+
if (format === 'unknown') {
|
|
14
|
+
throw new Error(`Unsupported file format: ${inputPath}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const renderer = new Renderer();
|
|
18
|
+
return await renderer.render(inputPath, format, options);
|
|
19
|
+
}
|
package/src/renderer.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { chromium, type Page } from 'playwright';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { resolve, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import type { FileFormat, ScreenshotOptions, ScreenshotResult } from './types.js';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
export class Renderer {
|
|
11
|
+
private getTemplatePath(format: FileFormat): string {
|
|
12
|
+
const templateMap: Record<FileFormat, string> = {
|
|
13
|
+
pdf: resolve(__dirname, '../templates/pdf.html'),
|
|
14
|
+
epub: resolve(__dirname, '../templates/epub.html'),
|
|
15
|
+
docx: resolve(__dirname, '../templates/docx.html'),
|
|
16
|
+
unknown: '',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const path = templateMap[format];
|
|
20
|
+
if (!path) {
|
|
21
|
+
throw new Error(`No template available for format: ${format}`);
|
|
22
|
+
}
|
|
23
|
+
return path;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private async injectDataIntoPage(
|
|
27
|
+
page: Page,
|
|
28
|
+
fileBase64: string,
|
|
29
|
+
pageNumber: number = 1
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
// Inject data into page globals before template loads
|
|
32
|
+
await page.addInitScript(({ fileBase64: fb64, pageNum }: { fileBase64: string; pageNum: number }) => {
|
|
33
|
+
// Override the placeholder values
|
|
34
|
+
(globalThis as any).fileBase64 = fb64;
|
|
35
|
+
(globalThis as any).pageNumber = pageNum;
|
|
36
|
+
}, { fileBase64, pageNum: pageNumber });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async render(
|
|
40
|
+
inputPath: string,
|
|
41
|
+
format: FileFormat,
|
|
42
|
+
options: ScreenshotOptions = {}
|
|
43
|
+
): Promise<ScreenshotResult> {
|
|
44
|
+
const {
|
|
45
|
+
output,
|
|
46
|
+
format: imageFormat = 'png',
|
|
47
|
+
width = 1920,
|
|
48
|
+
height = 1080,
|
|
49
|
+
page: pageNumber = 1,
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
const outputPath = output || inputPath.replace(/\.[^.]+$/, `.${imageFormat}`);
|
|
53
|
+
|
|
54
|
+
// Launch browser
|
|
55
|
+
const browser = await chromium.launch({
|
|
56
|
+
headless: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const page = await browser.newPage({
|
|
61
|
+
viewport: { width, height },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Read and encode file as base64
|
|
65
|
+
const fileData = await readFile(inputPath);
|
|
66
|
+
const fileBase64 = fileData.toString('base64');
|
|
67
|
+
|
|
68
|
+
// Inject data before loading template
|
|
69
|
+
await this.injectDataIntoPage(page, fileBase64, pageNumber);
|
|
70
|
+
|
|
71
|
+
// Load template
|
|
72
|
+
const templatePath = this.getTemplatePath(format);
|
|
73
|
+
await page.goto(`file://${templatePath}`);
|
|
74
|
+
|
|
75
|
+
// Wait for render complete and get metadata
|
|
76
|
+
const metadata = await page.evaluate(async () => {
|
|
77
|
+
const renderComplete = (globalThis as any).renderComplete;
|
|
78
|
+
|
|
79
|
+
if (!renderComplete) {
|
|
80
|
+
throw new Error('window.renderComplete not found');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Await the promise to get metadata
|
|
84
|
+
return await renderComplete;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Resize viewport to match actual rendered content
|
|
88
|
+
await page.setViewportSize({
|
|
89
|
+
width: metadata.width,
|
|
90
|
+
height: metadata.height,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Take screenshot at exact rendered size
|
|
94
|
+
await page.screenshot({
|
|
95
|
+
path: outputPath,
|
|
96
|
+
type: imageFormat as 'png' | 'jpeg',
|
|
97
|
+
fullPage: false,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await browser.close();
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
path: outputPath,
|
|
104
|
+
format: imageFormat,
|
|
105
|
+
width: metadata.width,
|
|
106
|
+
height: metadata.height,
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
await browser.close();
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ScreenshotOptions {
|
|
2
|
+
output?: string;
|
|
3
|
+
format?: 'png' | 'jpeg' | 'webp';
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
page?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ScreenshotResult {
|
|
10
|
+
path: string;
|
|
11
|
+
format: string;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RenderMetadata {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
pageCount: number;
|
|
20
|
+
pageNumber: number;
|
|
21
|
+
scale: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type FileFormat = 'pdf' | 'epub' | 'docx' | 'unknown';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"resolveJsonModule": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|