@browserless/pdf 13.6.1 → 13.6.2
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/package.json +4 -4
- package/src/index.js +98 -75
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@browserless/pdf",
|
|
3
3
|
"description": "Convert websites to high-quality PDFs with customizable margins, background printing, and optimized scaling.",
|
|
4
4
|
"homepage": "https://browserless.js.org/#/?id=pdfurl-options",
|
|
5
|
-
"version": "13.6.
|
|
5
|
+
"version": "13.6.2",
|
|
6
6
|
"main": "src",
|
|
7
7
|
"author": {
|
|
8
8
|
"email": "hello@microlink.io",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"website-to-pdf"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@browserless/goto": "13.6.
|
|
34
|
-
"@browserless/screenshot": "13.6.
|
|
33
|
+
"@browserless/goto": "13.6.2",
|
|
34
|
+
"@browserless/screenshot": "13.6.2",
|
|
35
35
|
"@kikobeats/time-span": "~1.0.12",
|
|
36
36
|
"debug-logfmt": "~1.4.10",
|
|
37
37
|
"p-reflect": "~2.1.0",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"test": "exit 0"
|
|
48
48
|
},
|
|
49
49
|
"license": "MIT",
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "7c7c6fee9e82050887d5499ed9dc3bac16c88c42"
|
|
51
51
|
}
|
package/src/index.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const timeSpan = require('@kikobeats/time-span')({ format: n => Math.round(n) })
|
|
4
|
+
const debug = require('debug-logfmt')('browserless:pdf')
|
|
5
|
+
const createGoto = require('@browserless/goto')
|
|
4
6
|
const pReflect = require('p-reflect')
|
|
7
|
+
|
|
5
8
|
const {
|
|
6
9
|
captureWithNavigationRetry,
|
|
7
10
|
isWhiteScreenshot,
|
|
8
11
|
waitForDomStability,
|
|
9
12
|
resolveWaitForDom,
|
|
10
|
-
|
|
13
|
+
SCREENSHOT_DEFAULT_OPTS
|
|
11
14
|
} = require('@browserless/screenshot')
|
|
12
|
-
|
|
13
|
-
const
|
|
15
|
+
|
|
16
|
+
const PDF_DEFAULT_OPTS = {
|
|
17
|
+
waitForDom: SCREENSHOT_DEFAULT_OPTS.waitForDom,
|
|
18
|
+
margin: '0.35cm',
|
|
19
|
+
scale: 0.65,
|
|
20
|
+
printBackground: true,
|
|
21
|
+
waitUntil: 'auto'
|
|
22
|
+
}
|
|
14
23
|
|
|
15
24
|
const getMargin = unit => {
|
|
16
25
|
if (!unit) return unit
|
|
@@ -26,83 +35,97 @@ const getMargin = unit => {
|
|
|
26
35
|
module.exports = ({ goto, ...gotoOpts } = {}) => {
|
|
27
36
|
goto = goto || createGoto(gotoOpts)
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
// Render an already-prepared page to a PDF buffer. Split out from the load so
|
|
39
|
+
// a single load can be reused across page-range chunks (microlink-api's
|
|
40
|
+
// parallel renderer) without re-navigating.
|
|
41
|
+
const render = (page, opts = {}) => {
|
|
42
|
+
const {
|
|
43
|
+
margin = PDF_DEFAULT_OPTS.margin,
|
|
44
|
+
scale = PDF_DEFAULT_OPTS.scale,
|
|
45
|
+
printBackground = PDF_DEFAULT_OPTS.printBackground,
|
|
46
|
+
waitUntil,
|
|
47
|
+
waitForDom,
|
|
48
|
+
...rest
|
|
49
|
+
} = opts
|
|
50
|
+
return captureWithNavigationRetry(
|
|
51
|
+
() => page.pdf({ ...rest, margin: getMargin(margin), printBackground, scale }),
|
|
52
|
+
{ page, goto, timeout: goto.timeouts.action(rest.timeout) }
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Navigate `page` to `url` and wait until it is ready to print: DOM stability
|
|
57
|
+
// plus, in `auto` mode, a screenshot poll that re-waits while the first paint
|
|
58
|
+
// is still blank.
|
|
59
|
+
const prepare = async (page, url, opts = {}) => {
|
|
60
|
+
const {
|
|
61
|
+
margin,
|
|
62
|
+
scale,
|
|
63
|
+
printBackground,
|
|
64
|
+
waitUntil = PDF_DEFAULT_OPTS.waitUntil,
|
|
65
|
+
waitForDom = PDF_DEFAULT_OPTS.waitForDom,
|
|
66
|
+
...rest
|
|
67
|
+
} = opts
|
|
68
|
+
const waitForDomOpts = resolveWaitForDom(waitForDom)
|
|
69
|
+
|
|
70
|
+
const waitForDomStabilityResult = async page => {
|
|
71
|
+
if (!waitForDomOpts) return
|
|
72
|
+
|
|
73
|
+
const result = await pReflect(page.evaluate(waitForDomStability, waitForDomOpts))
|
|
74
|
+
debug(
|
|
75
|
+
'waitForDomStability',
|
|
76
|
+
result.isRejected
|
|
77
|
+
? { ...waitForDomOpts, error: result.reason.message || result.reason }
|
|
78
|
+
: { ...waitForDomOpts, ...result.value }
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (waitUntil !== 'auto') {
|
|
83
|
+
await goto(page, { ...rest, url, waitUntil })
|
|
84
|
+
await waitForDomStabilityResult(page)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await goto(page, { ...rest, url, waitUntil, waitUntilAuto })
|
|
89
|
+
async function waitUntilAuto (page) {
|
|
90
|
+
await waitForDomStabilityResult(page)
|
|
91
|
+
const timeout = goto.timeouts.action(rest.timeout)
|
|
92
|
+
let isWhite = false
|
|
93
|
+
let retry = -1
|
|
94
|
+
|
|
95
|
+
const timePdf = timeSpan()
|
|
96
|
+
|
|
97
|
+
do {
|
|
98
|
+
++retry
|
|
99
|
+
const screenshotTime = timeSpan()
|
|
100
|
+
const screenshot = await captureWithNavigationRetry(
|
|
46
101
|
() =>
|
|
47
|
-
page.
|
|
48
|
-
...
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
102
|
+
page.screenshot({
|
|
103
|
+
...rest,
|
|
104
|
+
optimizeForSpeed: true,
|
|
105
|
+
type: 'jpeg',
|
|
106
|
+
quality: 30
|
|
52
107
|
}),
|
|
53
|
-
{ page, goto, timeout
|
|
108
|
+
{ page, goto, timeout }
|
|
54
109
|
)
|
|
110
|
+
isWhite = await isWhiteScreenshot(screenshot)
|
|
111
|
+
if (isWhite) await goto.waitUntilAuto(page, { timeout: rest.timeout })
|
|
112
|
+
debug('retry', { waitUntil, isWhite, retry, duration: screenshotTime() })
|
|
113
|
+
} while (isWhite && timePdf() < timeout)
|
|
55
114
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const result = await pReflect(page.evaluate(waitForDomStability, waitForDomOpts))
|
|
60
|
-
debug(
|
|
61
|
-
'waitForDomStability',
|
|
62
|
-
result.isRejected
|
|
63
|
-
? { ...waitForDomOpts, error: result.reason.message || result.reason }
|
|
64
|
-
: { ...waitForDomOpts, ...result.value }
|
|
65
|
-
)
|
|
66
|
-
}
|
|
115
|
+
debug({ waitUntil, isWhite, timeout, duration: require('pretty-ms')(timePdf()) })
|
|
116
|
+
}
|
|
117
|
+
}
|
|
67
118
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
await goto(page, { ...opts, url, waitUntil, waitUntilAuto })
|
|
74
|
-
async function waitUntilAuto (page) {
|
|
75
|
-
await waitForDomStabilityResult(page)
|
|
76
|
-
const timeout = goto.timeouts.action(opts.timeout)
|
|
77
|
-
let isWhite = false
|
|
78
|
-
let retry = -1
|
|
79
|
-
|
|
80
|
-
const timePdf = timeSpan()
|
|
81
|
-
|
|
82
|
-
do {
|
|
83
|
-
++retry
|
|
84
|
-
const screenshotTime = timeSpan()
|
|
85
|
-
const screenshot = await captureWithNavigationRetry(
|
|
86
|
-
() =>
|
|
87
|
-
page.screenshot({
|
|
88
|
-
...opts,
|
|
89
|
-
optimizeForSpeed: true,
|
|
90
|
-
type: 'jpeg',
|
|
91
|
-
quality: 30
|
|
92
|
-
}),
|
|
93
|
-
{ page, goto, timeout }
|
|
94
|
-
)
|
|
95
|
-
isWhite = await isWhiteScreenshot(screenshot)
|
|
96
|
-
if (isWhite) await goto.waitUntilAuto(page, { timeout: opts.timeout })
|
|
97
|
-
debug('retry', { waitUntil, isWhite, retry, duration: screenshotTime() })
|
|
98
|
-
} while (isWhite && timePdf() < timeout)
|
|
99
|
-
|
|
100
|
-
debug({ waitUntil, isWhite, timeout, duration: require('pretty-ms')(timePdf()) })
|
|
101
|
-
}
|
|
102
|
-
pdfBuffer = await generatePdf(page)
|
|
119
|
+
const pdf =
|
|
120
|
+
page =>
|
|
121
|
+
async (url, opts = {}) => {
|
|
122
|
+
await prepare(page, url, opts)
|
|
123
|
+
return render(page, opts)
|
|
103
124
|
}
|
|
104
125
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
126
|
+
pdf.prepare = prepare
|
|
127
|
+
pdf.render = render
|
|
128
|
+
return pdf
|
|
108
129
|
}
|
|
130
|
+
|
|
131
|
+
module.exports.DEFAULT_OPTS = PDF_DEFAULT_OPTS
|