screenitshot 0.6.0 → 0.7.1

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.js CHANGED
@@ -1,34 +1,83 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import { screenshot } from './index.js';
4
- import { readFile } from 'fs/promises';
5
- import { resolve, dirname } from 'path';
3
+ import { screenshot, detectFormat } from './index.js';
4
+ import { readFile, writeFile, access } from 'fs/promises';
5
+ import { resolve, dirname, basename, extname, join } from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = dirname(__filename);
9
9
  const packageJson = JSON.parse(await readFile(resolve(__dirname, '../package.json'), 'utf-8'));
10
+ /**
11
+ * Generate a unique output path using macOS-style duplicate naming.
12
+ * If basePath exists, returns basePath with ' (1)', ' (2)', etc. suffix.
13
+ * Example: document.png -> document (1).png -> document (2).png
14
+ */
15
+ async function getUniqueOutputPath(basePath) {
16
+ try {
17
+ await access(basePath);
18
+ }
19
+ catch {
20
+ // File doesn't exist, use the base path
21
+ return basePath;
22
+ }
23
+ const dir = dirname(basePath);
24
+ const ext = extname(basePath);
25
+ const stem = basename(basePath, ext);
26
+ let counter = 1;
27
+ // eslint-disable-next-line no-constant-condition
28
+ while (true) {
29
+ const newPath = join(dir, `${stem} (${counter})${ext}`);
30
+ try {
31
+ await access(newPath);
32
+ counter++;
33
+ }
34
+ catch {
35
+ return newPath;
36
+ }
37
+ }
38
+ }
10
39
  const program = new Command();
11
40
  program
12
41
  .name('screenitshot')
13
42
  .description('Convert various file formats to high-quality screenshots')
14
43
  .version(packageJson.version)
15
44
  .argument('<input>', 'Input file path')
16
- .argument('[output]', 'Output image path')
17
45
  .option('-f, --format <format>', 'Output image format (png, jpeg, webp)', 'png')
18
- .option('-w, --width <width>', 'Viewport width', '1280')
19
- .option('-h, --height <height>', 'Viewport height', '960')
46
+ .option('-w, --width <width>', 'Viewport width')
47
+ .option('-H, --height <height>', 'Viewport height')
20
48
  .option('-p, --page <page>', 'Page number for multi-page documents', '1')
21
- .action(async (input, output, options) => {
49
+ .action(async (input, options) => {
22
50
  try {
51
+ // Check input file exists and detect format
52
+ let inputData;
53
+ try {
54
+ inputData = await readFile(input);
55
+ }
56
+ catch {
57
+ console.error(`Error: Input file not found: ${input}`);
58
+ process.exit(1);
59
+ }
60
+ const inputFormat = await detectFormat(input);
61
+ if (inputFormat === 'unknown') {
62
+ console.error(`Error: Unsupported file format: ${input}`);
63
+ process.exit(1);
64
+ }
65
+ // Determine output path (same folder as input, with unique name)
66
+ const dir = dirname(input);
67
+ const stem = basename(input, extname(input));
68
+ const baseOutput = join(dir, `${stem}.${options.format}`);
69
+ const outputPath = await getUniqueOutputPath(baseOutput);
23
70
  console.log(`Converting ${input}...`);
24
- const result = await screenshot(input, {
25
- output,
71
+ const result = await screenshot(inputData, inputFormat, {
26
72
  format: options.format,
27
- width: parseInt(options.width),
28
- height: parseInt(options.height),
73
+ width: options.width ? parseInt(options.width) : undefined,
74
+ height: options.height ? parseInt(options.height) : undefined,
29
75
  page: parseInt(options.page),
76
+ fileName: basename(input),
30
77
  });
31
- console.log(`✓ Screenshot saved to ${result.path}`);
78
+ // Write output to file
79
+ await writeFile(outputPath, result.data);
80
+ console.log(`✓ Screenshot saved to ${outputPath}`);
32
81
  console.log(` Renderer: ${result.renderer}`);
33
82
  console.log(` Format: ${result.format}`);
34
83
  console.log(` Size: ${result.width}x${result.height}`);
@@ -36,16 +85,11 @@ program
36
85
  catch (error) {
37
86
  const err = error;
38
87
  // User-friendly error messages
39
- if (err.message.includes('ENOENT') || err.message.includes('no such file')) {
40
- console.error(`Error: Input file not found: ${input}`);
41
- }
42
- else if (err.message.includes('Unsupported file format')) {
88
+ if (err.message.includes('Unknown format')) {
43
89
  console.error(`Error: ${err.message}`);
44
- console.error('Supported formats: PDF');
45
90
  }
46
91
  else if (err.message.includes('No template available')) {
47
92
  console.error(`Error: Format not yet supported`);
48
- console.error('Currently supported: PDF');
49
93
  }
50
94
  else if (err.message.includes('page')) {
51
95
  console.error(`Error: Invalid page number or page not found`);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,MAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAC/D,CAAC;AAEF;;;;GAIG;AACH,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACjD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;QACxC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAErC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,iDAAiD;IACjD,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,cAAc,CAAC;KACpB,WAAW,CAAC,0DAA0D,CAAC;KACvE,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;KAC5B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;KACtC,MAAM,CAAC,uBAAuB,EAAE,uCAAuC,EAAE,KAAK,CAAC;KAC/E,MAAM,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;KAC/C,MAAM,CAAC,uBAAuB,EAAE,iBAAiB,CAAC;KAClD,MAAM,CAAC,mBAAmB,EAAE,sCAAsC,EAAE,GAAG,CAAC;KACxE,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,OAA2F,EAAE,EAAE;IAC3H,IAAI,CAAC;QACH,4CAA4C;QAC5C,IAAI,SAAiB,CAAC;QACtB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,KAAK,CAAC,mCAAmC,KAAK,EAAE,CAAC,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,iEAAiE;QACjE,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,CAAC;QAEzD,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,KAAK,CAAC,CAAC;QAEtC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,WAAW,EAAE;YACtD,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;YAC1D,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;YAC7D,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;YAC5B,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC;SAC1B,CAAC,CAAC;QAEH,uBAAuB;QACvB,MAAM,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAEzC,OAAO,CAAC,GAAG,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAc,CAAC;QAE3B,+BAA+B;QAC/B,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC3C,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;YACzD,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACnD,CAAC;aAAM,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC9D,iCAAiC;YACjC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC1C,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAEvC,iCAAiC;YACjC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,13 @@
1
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>;
2
+ export { type ScreenshotOptions, type ScreenshotResult, type FileFormat, resolveFormat } from './types.js';
3
+ export { detectFormat } from './detector.js';
4
+ /**
5
+ * Convert input data to a screenshot image.
6
+ *
7
+ * @param input - Input data: Buffer for documents, or URL string for 'url' format
8
+ * @param inputFormat - Input format as slug (e.g., 'pdf') or MIME type (e.g., 'application/pdf')
9
+ * @param options - Optional screenshot options (format, width, height, page, fileName)
10
+ * @returns ScreenshotResult with image data buffer and dimensions
11
+ */
12
+ export declare function screenshot(input: Buffer | string, inputFormat: string, options?: ScreenshotOptions): Promise<ScreenshotResult>;
4
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGtE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3G,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAK3B"}
package/dist/index.js CHANGED
@@ -1,11 +1,18 @@
1
- import { detectFormat } from './detector.js';
2
1
  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
- }
2
+ import { resolveFormat } from './types.js';
3
+ export { resolveFormat } from './types.js';
4
+ export { detectFormat } from './detector.js';
5
+ /**
6
+ * Convert input data to a screenshot image.
7
+ *
8
+ * @param input - Input data: Buffer for documents, or URL string for 'url' format
9
+ * @param inputFormat - Input format as slug (e.g., 'pdf') or MIME type (e.g., 'application/pdf')
10
+ * @param options - Optional screenshot options (format, width, height, page, fileName)
11
+ * @returns ScreenshotResult with image data buffer and dimensions
12
+ */
13
+ export async function screenshot(input, inputFormat, options = {}) {
14
+ const format = resolveFormat(inputFormat);
8
15
  const renderer = new Renderer();
9
- return await renderer.render(inputPath, format, options);
16
+ return await renderer.render(input, format, options);
10
17
  }
11
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAkE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3G,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAsB,EACtB,WAAmB,EACnB,UAA6B,EAAE;IAE/B,MAAM,MAAM,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC"}
@@ -2,6 +2,6 @@ import type { FileFormat, ScreenshotOptions, ScreenshotResult } from './types.js
2
2
  export declare class Renderer {
3
3
  private getTemplatePath;
4
4
  private injectDataIntoPage;
5
- render(inputPath: string, format: FileFormat, options?: ScreenshotOptions): Promise<ScreenshotResult>;
5
+ render(input: Buffer | string, format: FileFormat, options?: ScreenshotOptions): Promise<ScreenshotResult>;
6
6
  }
7
7
  //# sourceMappingURL=renderer.d.ts.map
@@ -1 +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;YA4BT,kBAAkB;IAe1B,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC;CAqJ7B"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKlF,qBAAa,QAAQ;IACnB,OAAO,CAAC,eAAe;YA4BT,kBAAkB;IAe1B,MAAM,CACV,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC;CA6J7B"}
package/dist/renderer.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { chromium } from 'playwright';
2
- import { readFile } from 'fs/promises';
3
- import { resolve, dirname, basename } from 'path';
2
+ import { resolve, dirname } from 'path';
4
3
  import { fileURLToPath } from 'url';
5
4
  const __filename = fileURLToPath(import.meta.url);
6
5
  const __dirname = dirname(__filename);
@@ -40,13 +39,12 @@ export class Renderer {
40
39
  globalThis.fileName = fName;
41
40
  }, { fileBase64, pageNum: pageNumber, fName: fileName });
42
41
  }
43
- async render(inputPath, format, options = {}) {
44
- const { output, format: imageFormat = 'png', width, height, page: pageNumber = 1, } = options;
42
+ async render(input, format, options = {}) {
43
+ const { format: imageFormat = 'png', width, height, page: pageNumber = 1, fileName = '', } = options;
45
44
  // Use small initial viewport - content will determine final size
46
45
  // For formats like XLSX, large viewport causes table to expand to fill it
47
46
  const initialWidth = width || 800;
48
47
  const initialHeight = height || 600;
49
- const outputPath = output || inputPath.replace(/\.[^.]+$/, `.${imageFormat}`);
50
48
  // Launch browser
51
49
  const browser = await chromium.launch({
52
50
  headless: true,
@@ -60,40 +58,44 @@ export class Renderer {
60
58
  });
61
59
  // Special handling for URL format - navigate directly to the URL
62
60
  if (format === 'url') {
63
- // Read URL from file (file contains just the URL string)
64
- const fileData = await readFile(inputPath);
65
- const url = fileData.toString('utf-8').trim();
61
+ // Input should be URL string (or buffer containing URL)
62
+ const url = Buffer.isBuffer(input)
63
+ ? input.toString('utf-8').trim()
64
+ : input.trim();
66
65
  // Set a reasonable viewport for webpage screenshots
67
66
  const webWidth = width || 1280;
68
67
  const webHeight = height || 800;
69
68
  await page.setViewportSize({ width: webWidth, height: webHeight });
70
69
  // Navigate to URL and wait for network idle
71
70
  await page.goto(url, { waitUntil: 'networkidle' });
72
- // Take screenshot
73
- await page.screenshot({
74
- path: outputPath,
71
+ // Take screenshot to buffer
72
+ const screenshotData = await page.screenshot({
75
73
  type: imageFormat,
76
74
  fullPage: false,
77
75
  });
78
76
  await browser.close();
79
77
  return {
80
- path: outputPath,
78
+ data: screenshotData,
81
79
  format: imageFormat,
82
80
  width: webWidth * deviceScaleFactor,
83
81
  height: webHeight * deviceScaleFactor,
84
82
  renderer: format,
85
83
  };
86
84
  }
87
- // Read and encode file as base64
88
- const fileData = await readFile(inputPath);
89
- const fileBase64 = fileData.toString('base64');
90
- // Inject data before loading template (include filename for code format)
91
- const fileName = basename(inputPath);
85
+ // Encode input as base64
86
+ let fileBase64;
87
+ if (Buffer.isBuffer(input)) {
88
+ fileBase64 = input.toString('base64');
89
+ }
90
+ else {
91
+ // String input for non-URL formats is treated as text content
92
+ fileBase64 = Buffer.from(input, 'utf-8').toString('base64');
93
+ }
94
+ // Inject data before loading template
92
95
  await this.injectDataIntoPage(page, fileBase64, pageNumber, fileName);
93
96
  // Load template
94
97
  const templatePath = this.getTemplatePath(format);
95
98
  await page.goto(`file://${templatePath}`);
96
- // Wait for render complete and get metadata
97
99
  const metadata = await page.evaluate(async () => {
98
100
  const renderComplete = globalThis.renderComplete;
99
101
  if (!renderComplete) {
@@ -105,6 +107,7 @@ export class Renderer {
105
107
  // Check if we need to clip (for EPUB content cropping)
106
108
  const clipX = metadata.clipX;
107
109
  const clipY = metadata.clipY;
110
+ let screenshotData;
108
111
  if (clipX !== undefined && clipY !== undefined) {
109
112
  // Resize viewport to ensure clip area is fully visible
110
113
  const requiredWidth = clipX + metadata.width;
@@ -116,8 +119,7 @@ export class Renderer {
116
119
  // Wait for layout to stabilize
117
120
  await page.waitForTimeout(100);
118
121
  // Use clip to capture just the content area
119
- await page.screenshot({
120
- path: outputPath,
122
+ screenshotData = await page.screenshot({
121
123
  type: imageFormat,
122
124
  clip: {
123
125
  x: clipX,
@@ -136,8 +138,7 @@ export class Renderer {
136
138
  // Wait for layout to stabilize after viewport resize
137
139
  await page.waitForTimeout(100);
138
140
  // Take screenshot at exact rendered size
139
- await page.screenshot({
140
- path: outputPath,
141
+ screenshotData = await page.screenshot({
141
142
  type: imageFormat,
142
143
  fullPage: false,
143
144
  });
@@ -147,7 +148,7 @@ export class Renderer {
147
148
  const actualWidth = metadata.width * deviceScaleFactor;
148
149
  const actualHeight = metadata.height * deviceScaleFactor;
149
150
  return {
150
- path: outputPath,
151
+ data: screenshotData,
151
152
  format: imageFormat,
152
153
  width: actualWidth,
153
154
  height: actualHeight,
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAGpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,OAAO,QAAQ;IACX,eAAe,CAAC,MAAkB;QACxC,MAAM,WAAW,GAA+B;YAC9C,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC;YAChD,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,wBAAwB,CAAC;YAClD,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,wBAAwB,CAAC;YAClD,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,wBAAwB,CAAC;YAClD,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,wBAAwB,CAAC;YAClD,EAAE,EAAE,OAAO,CAAC,SAAS,EAAE,sBAAsB,CAAC;YAC9C,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,wBAAwB,CAAC;YAClD,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC;YAChD,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC;YAChD,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,yBAAyB,CAAC;YACpD,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC;YAChD,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,wBAAwB,CAAC;YAClD,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC;YAChD,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC;YAChD,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,2BAA2B,CAAC;YACxD,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,uBAAuB,CAAC;YAChD,OAAO,EAAE,EAAE;SACZ,CAAC;QAEF,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,qCAAqC,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC9B,IAAU,EACV,UAAkB,EAClB,aAAqB,CAAC,EACtB,WAAmB,EAAE;QAErB,sDAAsD;QACtD,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAA0D,EAAE,EAAE;YACxH,kCAAkC;YACjC,UAAiD,CAAC,UAAU,GAAG,IAAI,CAAC;YACpE,UAAiD,CAAC,UAAU,GAAG,OAAO,CAAC;YACvE,UAAiD,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtE,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAAsB,EACtB,MAAkB,EAClB,UAA6B,EAAE;QAE/B,MAAM,EACJ,MAAM,EAAE,WAAW,GAAG,KAAK,EAC3B,KAAK,EACL,MAAM,EACN,IAAI,EAAE,UAAU,GAAG,CAAC,EACpB,QAAQ,GAAG,EAAE,GACd,GAAG,OAAO,CAAC;QAEZ,iEAAiE;QACjE,0EAA0E;QAC1E,MAAM,YAAY,GAAG,KAAK,IAAI,GAAG,CAAC;QAClC,MAAM,aAAa,GAAG,MAAM,IAAI,GAAG,CAAC;QAEpC,iBAAiB;QACjB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;YACpC,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,yEAAyE;YACzE,MAAM,iBAAiB,GAAG,CAAC,CAAC;YAE5B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC;gBACjC,QAAQ,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE;gBACxD,iBAAiB;aAClB,CAAC,CAAC;YAEH,iEAAiE;YACjE,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;gBACrB,wDAAwD;gBACxD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;oBAChC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE;oBAChC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;gBAEjB,oDAAoD;gBACpD,MAAM,QAAQ,GAAG,KAAK,IAAI,IAAI,CAAC;gBAC/B,MAAM,SAAS,GAAG,MAAM,IAAI,GAAG,CAAC;gBAChC,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;gBAEnE,4CAA4C;gBAC5C,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;gBAEnD,4BAA4B;gBAC5B,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;oBAC3C,IAAI,EAAE,WAA6B;oBACnC,QAAQ,EAAE,KAAK;iBAChB,CAAC,CAAC;gBAEH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;gBAEtB,OAAO;oBACL,IAAI,EAAE,cAAc;oBACpB,MAAM,EAAE,WAAW;oBACnB,KAAK,EAAE,QAAQ,GAAG,iBAAiB;oBACnC,MAAM,EAAE,SAAS,GAAG,iBAAiB;oBACrC,QAAQ,EAAE,MAAM;iBACjB,CAAC;YACJ,CAAC;YAED,yBAAyB;YACzB,IAAI,UAAkB,CAAC;YACvB,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,8DAA8D;gBAC9D,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC9D,CAAC;YAED,sCAAsC;YACtC,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;YAEtE,gBAAgB;YAChB,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAClD,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,YAAY,EAAE,CAAC,CAAC;YAS1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;gBAC9C,MAAM,cAAc,GAAI,UAAiD,CAAC,cAAmD,CAAC;gBAE9H,IAAI,CAAC,cAAc,EAAE,CAAC;oBACpB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;gBACrD,CAAC;gBAED,oCAAoC;gBACpC,OAAO,MAAM,cAAc,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,uDAAuD;YACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;YAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;YAE7B,IAAI,cAAsB,CAAC;YAE3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC/C,uDAAuD;gBACvD,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;gBAC7C,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAC/C,MAAM,IAAI,CAAC,eAAe,CAAC;oBACzB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,YAAY,CAAC;oBAC5C,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,aAAa,CAAC;iBAChD,CAAC,CAAC;gBAEH,+BAA+B;gBAC/B,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;gBAE/B,4CAA4C;gBAC5C,cAAc,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;oBACrC,IAAI,EAAE,WAA6B;oBACnC,IAAI,EAAE;wBACJ,CAAC,EAAE,KAAK;wBACR,CAAC,EAAE,KAAK;wBACR,KAAK,EAAE,QAAQ,CAAC,KAAK;wBACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;qBACxB;iBACF,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,mDAAmD;gBACnD,MAAM,IAAI,CAAC,eAAe,CAAC;oBACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;oBACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;iBACxB,CAAC,CAAC;gBAEH,qDAAqD;gBACrD,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;gBAE/B,yCAAyC;gBACzC,cAAc,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;oBACrC,IAAI,EAAE,WAA6B;oBACnC,QAAQ,EAAE,KAAK;iBAChB,CAAC,CAAC;YACL,CAAC;YAED,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YAEtB,oDAAoD;YACpD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,GAAG,iBAAiB,CAAC;YACvD,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,GAAG,iBAAiB,CAAC;YAEzD,OAAO;gBACL,IAAI,EAAE,cAAc;gBACpB,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,WAAW;gBAClB,MAAM,EAAE,YAAY;gBACpB,QAAQ,EAAE,MAAM;aACjB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACtB,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
package/dist/types.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  export interface ScreenshotOptions {
2
- output?: string;
3
2
  format?: 'png' | 'jpeg' | 'webp';
4
3
  width?: number;
5
4
  height?: number;
6
5
  page?: number;
6
+ fileName?: string;
7
7
  }
8
8
  export interface ScreenshotResult {
9
- path: string;
9
+ data: Buffer;
10
10
  format: string;
11
11
  width: number;
12
12
  height: number;
@@ -20,4 +20,10 @@ export interface RenderMetadata {
20
20
  scale: number;
21
21
  }
22
22
  export type FileFormat = 'pdf' | 'epub' | 'docx' | 'xlsx' | 'pptx' | 'md' | 'html' | 'csv' | 'rtf' | 'ipynb' | 'tex' | 'code' | 'url' | 'mmd' | 'geojson' | 'gpx' | 'unknown';
23
+ export declare const MIME_TO_FORMAT: Record<string, FileFormat>;
24
+ export declare const SLUG_TO_FORMAT: Record<string, FileFormat>;
25
+ /**
26
+ * Resolve a format string (MIME type or slug) to a FileFormat.
27
+ */
28
+ export declare function resolveFormat(formatStr: string): FileFormat;
23
29
  //# sourceMappingURL=types.d.ts.map
@@ -1 +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;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;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,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,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;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;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;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;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,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC;AAG9K,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAgBrD,CAAC;AAGF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAsBrD,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAa3D"}
package/dist/types.js CHANGED
@@ -1,2 +1,58 @@
1
- export {};
1
+ // Mapping from MIME types to FileFormat
2
+ export const MIME_TO_FORMAT = {
3
+ 'application/pdf': 'pdf',
4
+ 'application/epub+zip': 'epub',
5
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
6
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
7
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
8
+ 'text/markdown': 'md',
9
+ 'text/html': 'html',
10
+ 'text/csv': 'csv',
11
+ 'application/rtf': 'rtf',
12
+ 'text/rtf': 'rtf',
13
+ 'application/x-ipynb+json': 'ipynb',
14
+ 'application/x-tex': 'tex',
15
+ 'text/x-tex': 'tex',
16
+ 'application/geo+json': 'geojson',
17
+ 'application/gpx+xml': 'gpx',
18
+ };
19
+ // Mapping from slug names to FileFormat
20
+ export const SLUG_TO_FORMAT = {
21
+ 'pdf': 'pdf',
22
+ 'epub': 'epub',
23
+ 'docx': 'docx',
24
+ 'xlsx': 'xlsx',
25
+ 'pptx': 'pptx',
26
+ 'md': 'md',
27
+ 'markdown': 'md',
28
+ 'html': 'html',
29
+ 'htm': 'html',
30
+ 'csv': 'csv',
31
+ 'rtf': 'rtf',
32
+ 'ipynb': 'ipynb',
33
+ 'jupyter': 'ipynb',
34
+ 'tex': 'tex',
35
+ 'latex': 'tex',
36
+ 'code': 'code',
37
+ 'url': 'url',
38
+ 'mmd': 'mmd',
39
+ 'mermaid': 'mmd',
40
+ 'geojson': 'geojson',
41
+ 'gpx': 'gpx',
42
+ };
43
+ /**
44
+ * Resolve a format string (MIME type or slug) to a FileFormat.
45
+ */
46
+ export function resolveFormat(formatStr) {
47
+ // Try slug first (more common)
48
+ const lowerFormat = formatStr.toLowerCase();
49
+ if (lowerFormat in SLUG_TO_FORMAT) {
50
+ return SLUG_TO_FORMAT[lowerFormat];
51
+ }
52
+ // Try MIME type
53
+ if (formatStr in MIME_TO_FORMAT) {
54
+ return MIME_TO_FORMAT[formatStr];
55
+ }
56
+ throw new Error(`Unknown format: ${formatStr}. Use a slug (e.g., 'pdf') or MIME type (e.g., 'application/pdf')`);
57
+ }
2
58
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0BA,wCAAwC;AACxC,MAAM,CAAC,MAAM,cAAc,GAA+B;IACxD,iBAAiB,EAAE,KAAK;IACxB,sBAAsB,EAAE,MAAM;IAC9B,yEAAyE,EAAE,MAAM;IACjF,mEAAmE,EAAE,MAAM;IAC3E,2EAA2E,EAAE,MAAM;IACnF,eAAe,EAAE,IAAI;IACrB,WAAW,EAAE,MAAM;IACnB,UAAU,EAAE,KAAK;IACjB,iBAAiB,EAAE,KAAK;IACxB,UAAU,EAAE,KAAK;IACjB,0BAA0B,EAAE,OAAO;IACnC,mBAAmB,EAAE,KAAK;IAC1B,YAAY,EAAE,KAAK;IACnB,sBAAsB,EAAE,SAAS;IACjC,qBAAqB,EAAE,KAAK;CAC7B,CAAC;AAEF,wCAAwC;AACxC,MAAM,CAAC,MAAM,cAAc,GAA+B;IACxD,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,IAAI;IACV,UAAU,EAAE,IAAI;IAChB,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,KAAK;IACZ,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,OAAO;IAChB,SAAS,EAAE,OAAO;IAClB,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,KAAK;IACZ,KAAK,EAAE,KAAK;IACZ,SAAS,EAAE,KAAK;IAChB,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,KAAK;CACb,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,+BAA+B;IAC/B,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;IAC5C,IAAI,WAAW,IAAI,cAAc,EAAE,CAAC;QAClC,OAAO,cAAc,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,gBAAgB;IAChB,IAAI,SAAS,IAAI,cAAc,EAAE,CAAC;QAChC,OAAO,cAAc,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,mBAAmB,SAAS,mEAAmE,CAAC,CAAC;AACnH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screenitshot",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Convert various file formats to high-quality screenshots",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/cli.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import { screenshot } from './index.js';
5
- import { readFile } from 'fs/promises';
6
- import { resolve, dirname } from 'path';
4
+ import { screenshot, detectFormat } from './index.js';
5
+ import { readFile, writeFile, access } from 'fs/promises';
6
+ import { resolve, dirname, basename, extname, join } from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
 
9
9
  const __filename = fileURLToPath(import.meta.url);
@@ -13,6 +13,36 @@ const packageJson = JSON.parse(
13
13
  await readFile(resolve(__dirname, '../package.json'), 'utf-8')
14
14
  );
15
15
 
16
+ /**
17
+ * Generate a unique output path using macOS-style duplicate naming.
18
+ * If basePath exists, returns basePath with ' (1)', ' (2)', etc. suffix.
19
+ * Example: document.png -> document (1).png -> document (2).png
20
+ */
21
+ async function getUniqueOutputPath(basePath: string): Promise<string> {
22
+ try {
23
+ await access(basePath);
24
+ } catch {
25
+ // File doesn't exist, use the base path
26
+ return basePath;
27
+ }
28
+
29
+ const dir = dirname(basePath);
30
+ const ext = extname(basePath);
31
+ const stem = basename(basePath, ext);
32
+
33
+ let counter = 1;
34
+ // eslint-disable-next-line no-constant-condition
35
+ while (true) {
36
+ const newPath = join(dir, `${stem} (${counter})${ext}`);
37
+ try {
38
+ await access(newPath);
39
+ counter++;
40
+ } catch {
41
+ return newPath;
42
+ }
43
+ }
44
+ }
45
+
16
46
  const program = new Command();
17
47
 
18
48
  program
@@ -20,24 +50,47 @@ program
20
50
  .description('Convert various file formats to high-quality screenshots')
21
51
  .version(packageJson.version)
22
52
  .argument('<input>', 'Input file path')
23
- .argument('[output]', 'Output image path')
24
53
  .option('-f, --format <format>', 'Output image format (png, jpeg, webp)', 'png')
25
- .option('-w, --width <width>', 'Viewport width', '1280')
26
- .option('-h, --height <height>', 'Viewport height', '960')
54
+ .option('-w, --width <width>', 'Viewport width')
55
+ .option('-H, --height <height>', 'Viewport height')
27
56
  .option('-p, --page <page>', 'Page number for multi-page documents', '1')
28
- .action(async (input: string, output: string | undefined, options: any) => {
57
+ .action(async (input: string, options: { format: 'png' | 'jpeg' | 'webp'; width?: string; height?: string; page: string }) => {
29
58
  try {
59
+ // Check input file exists and detect format
60
+ let inputData: Buffer;
61
+ try {
62
+ inputData = await readFile(input);
63
+ } catch {
64
+ console.error(`Error: Input file not found: ${input}`);
65
+ process.exit(1);
66
+ }
67
+
68
+ const inputFormat = await detectFormat(input);
69
+ if (inputFormat === 'unknown') {
70
+ console.error(`Error: Unsupported file format: ${input}`);
71
+ process.exit(1);
72
+ }
73
+
74
+ // Determine output path (same folder as input, with unique name)
75
+ const dir = dirname(input);
76
+ const stem = basename(input, extname(input));
77
+ const baseOutput = join(dir, `${stem}.${options.format}`);
78
+ const outputPath = await getUniqueOutputPath(baseOutput);
79
+
30
80
  console.log(`Converting ${input}...`);
31
81
 
32
- const result = await screenshot(input, {
33
- output,
82
+ const result = await screenshot(inputData, inputFormat, {
34
83
  format: options.format,
35
- width: parseInt(options.width),
36
- height: parseInt(options.height),
84
+ width: options.width ? parseInt(options.width) : undefined,
85
+ height: options.height ? parseInt(options.height) : undefined,
37
86
  page: parseInt(options.page),
87
+ fileName: basename(input),
38
88
  });
39
89
 
40
- console.log(`✓ Screenshot saved to ${result.path}`);
90
+ // Write output to file
91
+ await writeFile(outputPath, result.data);
92
+
93
+ console.log(`✓ Screenshot saved to ${outputPath}`);
41
94
  console.log(` Renderer: ${result.renderer}`);
42
95
  console.log(` Format: ${result.format}`);
43
96
  console.log(` Size: ${result.width}x${result.height}`);
@@ -45,14 +98,10 @@ program
45
98
  const err = error as Error;
46
99
 
47
100
  // User-friendly error messages
48
- if (err.message.includes('ENOENT') || err.message.includes('no such file')) {
49
- console.error(`Error: Input file not found: ${input}`);
50
- } else if (err.message.includes('Unsupported file format')) {
101
+ if (err.message.includes('Unknown format')) {
51
102
  console.error(`Error: ${err.message}`);
52
- console.error('Supported formats: PDF');
53
103
  } else if (err.message.includes('No template available')) {
54
104
  console.error(`Error: Format not yet supported`);
55
- console.error('Currently supported: PDF');
56
105
  } else if (err.message.includes('page')) {
57
106
  console.error(`Error: Invalid page number or page not found`);
58
107
  // Show stack trace in debug mode
package/src/index.ts CHANGED
@@ -1,19 +1,25 @@
1
- import { detectFormat } from './detector.js';
2
1
  import { Renderer } from './renderer.js';
3
2
  import type { ScreenshotOptions, ScreenshotResult } from './types.js';
3
+ import { resolveFormat } from './types.js';
4
4
 
5
- export { type ScreenshotOptions, type ScreenshotResult, type FileFormat } from './types.js';
5
+ export { type ScreenshotOptions, type ScreenshotResult, type FileFormat, resolveFormat } from './types.js';
6
+ export { detectFormat } from './detector.js';
6
7
 
8
+ /**
9
+ * Convert input data to a screenshot image.
10
+ *
11
+ * @param input - Input data: Buffer for documents, or URL string for 'url' format
12
+ * @param inputFormat - Input format as slug (e.g., 'pdf') or MIME type (e.g., 'application/pdf')
13
+ * @param options - Optional screenshot options (format, width, height, page, fileName)
14
+ * @returns ScreenshotResult with image data buffer and dimensions
15
+ */
7
16
  export async function screenshot(
8
- inputPath: string,
17
+ input: Buffer | string,
18
+ inputFormat: string,
9
19
  options: ScreenshotOptions = {}
10
20
  ): Promise<ScreenshotResult> {
11
- const format = await detectFormat(inputPath);
12
-
13
- if (format === 'unknown') {
14
- throw new Error(`Unsupported file format: ${inputPath}`);
15
- }
21
+ const format = resolveFormat(inputFormat);
16
22
 
17
23
  const renderer = new Renderer();
18
- return await renderer.render(inputPath, format, options);
24
+ return await renderer.render(input, format, options);
19
25
  }
package/src/renderer.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { chromium, type Page } from 'playwright';
2
- import { readFile } from 'fs/promises';
3
- import { resolve, dirname, basename } from 'path';
2
+ import { resolve, dirname } from 'path';
4
3
  import { fileURLToPath } from 'url';
5
4
  import type { FileFormat, ScreenshotOptions, ScreenshotResult } from './types.js';
6
5
 
@@ -45,23 +44,23 @@ export class Renderer {
45
44
  // Inject data into page globals before template loads
46
45
  await page.addInitScript(({ fileBase64: fb64, pageNum, fName }: { fileBase64: string; pageNum: number; fName: string }) => {
47
46
  // Override the placeholder values
48
- (globalThis as any).fileBase64 = fb64;
49
- (globalThis as any).pageNumber = pageNum;
50
- (globalThis as any).fileName = fName;
47
+ (globalThis as unknown as Record<string, unknown>).fileBase64 = fb64;
48
+ (globalThis as unknown as Record<string, unknown>).pageNumber = pageNum;
49
+ (globalThis as unknown as Record<string, unknown>).fileName = fName;
51
50
  }, { fileBase64, pageNum: pageNumber, fName: fileName });
52
51
  }
53
52
 
54
53
  async render(
55
- inputPath: string,
54
+ input: Buffer | string,
56
55
  format: FileFormat,
57
56
  options: ScreenshotOptions = {}
58
57
  ): Promise<ScreenshotResult> {
59
58
  const {
60
- output,
61
59
  format: imageFormat = 'png',
62
60
  width,
63
61
  height,
64
62
  page: pageNumber = 1,
63
+ fileName = '',
65
64
  } = options;
66
65
 
67
66
  // Use small initial viewport - content will determine final size
@@ -69,8 +68,6 @@ export class Renderer {
69
68
  const initialWidth = width || 800;
70
69
  const initialHeight = height || 600;
71
70
 
72
- const outputPath = output || inputPath.replace(/\.[^.]+$/, `.${imageFormat}`);
73
-
74
71
  // Launch browser
75
72
  const browser = await chromium.launch({
76
73
  headless: true,
@@ -87,9 +84,10 @@ export class Renderer {
87
84
 
88
85
  // Special handling for URL format - navigate directly to the URL
89
86
  if (format === 'url') {
90
- // Read URL from file (file contains just the URL string)
91
- const fileData = await readFile(inputPath);
92
- const url = fileData.toString('utf-8').trim();
87
+ // Input should be URL string (or buffer containing URL)
88
+ const url = Buffer.isBuffer(input)
89
+ ? input.toString('utf-8').trim()
90
+ : input.trim();
93
91
 
94
92
  // Set a reasonable viewport for webpage screenshots
95
93
  const webWidth = width || 1280;
@@ -99,9 +97,8 @@ export class Renderer {
99
97
  // Navigate to URL and wait for network idle
100
98
  await page.goto(url, { waitUntil: 'networkidle' });
101
99
 
102
- // Take screenshot
103
- await page.screenshot({
104
- path: outputPath,
100
+ // Take screenshot to buffer
101
+ const screenshotData = await page.screenshot({
105
102
  type: imageFormat as 'png' | 'jpeg',
106
103
  fullPage: false,
107
104
  });
@@ -109,7 +106,7 @@ export class Renderer {
109
106
  await browser.close();
110
107
 
111
108
  return {
112
- path: outputPath,
109
+ data: screenshotData,
113
110
  format: imageFormat,
114
111
  width: webWidth * deviceScaleFactor,
115
112
  height: webHeight * deviceScaleFactor,
@@ -117,12 +114,16 @@ export class Renderer {
117
114
  };
118
115
  }
119
116
 
120
- // Read and encode file as base64
121
- const fileData = await readFile(inputPath);
122
- const fileBase64 = fileData.toString('base64');
117
+ // Encode input as base64
118
+ let fileBase64: string;
119
+ if (Buffer.isBuffer(input)) {
120
+ fileBase64 = input.toString('base64');
121
+ } else {
122
+ // String input for non-URL formats is treated as text content
123
+ fileBase64 = Buffer.from(input, 'utf-8').toString('base64');
124
+ }
123
125
 
124
- // Inject data before loading template (include filename for code format)
125
- const fileName = basename(inputPath);
126
+ // Inject data before loading template
126
127
  await this.injectDataIntoPage(page, fileBase64, pageNumber, fileName);
127
128
 
128
129
  // Load template
@@ -130,8 +131,14 @@ export class Renderer {
130
131
  await page.goto(`file://${templatePath}`);
131
132
 
132
133
  // Wait for render complete and get metadata
134
+ interface PageMetadata {
135
+ width: number;
136
+ height: number;
137
+ clipX?: number;
138
+ clipY?: number;
139
+ }
133
140
  const metadata = await page.evaluate(async () => {
134
- const renderComplete = (globalThis as any).renderComplete;
141
+ const renderComplete = (globalThis as unknown as Record<string, unknown>).renderComplete as Promise<PageMetadata> | undefined;
135
142
 
136
143
  if (!renderComplete) {
137
144
  throw new Error('window.renderComplete not found');
@@ -142,8 +149,10 @@ export class Renderer {
142
149
  });
143
150
 
144
151
  // Check if we need to clip (for EPUB content cropping)
145
- const clipX = (metadata as any).clipX;
146
- const clipY = (metadata as any).clipY;
152
+ const clipX = metadata.clipX;
153
+ const clipY = metadata.clipY;
154
+
155
+ let screenshotData: Buffer;
147
156
 
148
157
  if (clipX !== undefined && clipY !== undefined) {
149
158
  // Resize viewport to ensure clip area is fully visible
@@ -158,8 +167,7 @@ export class Renderer {
158
167
  await page.waitForTimeout(100);
159
168
 
160
169
  // Use clip to capture just the content area
161
- await page.screenshot({
162
- path: outputPath,
170
+ screenshotData = await page.screenshot({
163
171
  type: imageFormat as 'png' | 'jpeg',
164
172
  clip: {
165
173
  x: clipX,
@@ -179,8 +187,7 @@ export class Renderer {
179
187
  await page.waitForTimeout(100);
180
188
 
181
189
  // Take screenshot at exact rendered size
182
- await page.screenshot({
183
- path: outputPath,
190
+ screenshotData = await page.screenshot({
184
191
  type: imageFormat as 'png' | 'jpeg',
185
192
  fullPage: false,
186
193
  });
@@ -193,7 +200,7 @@ export class Renderer {
193
200
  const actualHeight = metadata.height * deviceScaleFactor;
194
201
 
195
202
  return {
196
- path: outputPath,
203
+ data: screenshotData,
197
204
  format: imageFormat,
198
205
  width: actualWidth,
199
206
  height: actualHeight,
package/src/types.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  export interface ScreenshotOptions {
2
- output?: string;
3
2
  format?: 'png' | 'jpeg' | 'webp';
4
3
  width?: number;
5
4
  height?: number;
6
5
  page?: number;
6
+ fileName?: string;
7
7
  }
8
8
 
9
9
  export interface ScreenshotResult {
10
- path: string;
10
+ data: Buffer;
11
11
  format: string;
12
12
  width: number;
13
13
  height: number;
@@ -23,3 +23,65 @@ export interface RenderMetadata {
23
23
  }
24
24
 
25
25
  export type FileFormat = 'pdf' | 'epub' | 'docx' | 'xlsx' | 'pptx' | 'md' | 'html' | 'csv' | 'rtf' | 'ipynb' | 'tex' | 'code' | 'url' | 'mmd' | 'geojson' | 'gpx' | 'unknown';
26
+
27
+ // Mapping from MIME types to FileFormat
28
+ export const MIME_TO_FORMAT: Record<string, FileFormat> = {
29
+ 'application/pdf': 'pdf',
30
+ 'application/epub+zip': 'epub',
31
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
32
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
33
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
34
+ 'text/markdown': 'md',
35
+ 'text/html': 'html',
36
+ 'text/csv': 'csv',
37
+ 'application/rtf': 'rtf',
38
+ 'text/rtf': 'rtf',
39
+ 'application/x-ipynb+json': 'ipynb',
40
+ 'application/x-tex': 'tex',
41
+ 'text/x-tex': 'tex',
42
+ 'application/geo+json': 'geojson',
43
+ 'application/gpx+xml': 'gpx',
44
+ };
45
+
46
+ // Mapping from slug names to FileFormat
47
+ export const SLUG_TO_FORMAT: Record<string, FileFormat> = {
48
+ 'pdf': 'pdf',
49
+ 'epub': 'epub',
50
+ 'docx': 'docx',
51
+ 'xlsx': 'xlsx',
52
+ 'pptx': 'pptx',
53
+ 'md': 'md',
54
+ 'markdown': 'md',
55
+ 'html': 'html',
56
+ 'htm': 'html',
57
+ 'csv': 'csv',
58
+ 'rtf': 'rtf',
59
+ 'ipynb': 'ipynb',
60
+ 'jupyter': 'ipynb',
61
+ 'tex': 'tex',
62
+ 'latex': 'tex',
63
+ 'code': 'code',
64
+ 'url': 'url',
65
+ 'mmd': 'mmd',
66
+ 'mermaid': 'mmd',
67
+ 'geojson': 'geojson',
68
+ 'gpx': 'gpx',
69
+ };
70
+
71
+ /**
72
+ * Resolve a format string (MIME type or slug) to a FileFormat.
73
+ */
74
+ export function resolveFormat(formatStr: string): FileFormat {
75
+ // Try slug first (more common)
76
+ const lowerFormat = formatStr.toLowerCase();
77
+ if (lowerFormat in SLUG_TO_FORMAT) {
78
+ return SLUG_TO_FORMAT[lowerFormat];
79
+ }
80
+
81
+ // Try MIME type
82
+ if (formatStr in MIME_TO_FORMAT) {
83
+ return MIME_TO_FORMAT[formatStr];
84
+ }
85
+
86
+ throw new Error(`Unknown format: ${formatStr}. Use a slug (e.g., 'pdf') or MIME type (e.g., 'application/pdf')`);
87
+ }