-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
index.js
156 lines (128 loc) Β· 4.01 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import FontStore from '@react-pdf/font';
import renderPDF from '@react-pdf/render';
import PDFDocument from '@react-pdf/pdfkit';
import layoutDocument from '@react-pdf/layout';
import createRenderer from './renderer';
import packageJson from '../package.json';
const { version } = packageJson;
const fontStore = new FontStore();
// We must keep a single renderer instance, otherwise React will complain
let renderer;
// The pdf instance acts as an event emitter for DOM usage.
// We only want to trigger an update when PDF content changes
const events = {};
const pdf = (initialValue) => {
const onChange = () => {
const listeners = events.change?.slice() || [];
for (let i = 0; i < listeners.length; i += 1) listeners[i]();
};
const container = { type: 'ROOT', document: null };
renderer = renderer || createRenderer({ onChange });
const mountNode = renderer.createContainer(container);
const updateContainer = (doc, callback) => {
renderer.updateContainer(doc, mountNode, null, callback);
};
if (initialValue) updateContainer(initialValue);
const render = async (compress = true) => {
const props = container.document.props || {};
const { pdfVersion, language, pageLayout, pageMode } = props;
const ctx = new PDFDocument({
compress,
pdfVersion,
lang: language,
displayTitle: true,
autoFirstPage: false,
pageLayout,
pageMode,
});
const layout = await layoutDocument(container.document, fontStore);
const fileStream = renderPDF(ctx, layout);
return { layout, fileStream };
};
const callOnRender = (params = {}) => {
if (container.document.props.onRender) {
container.document.props.onRender(params);
}
};
const toBlob = async () => {
const chunks = [];
const { layout: _INTERNAL__LAYOUT__DATA_, fileStream: instance } =
await render();
return new Promise((resolve, reject) => {
instance.on('data', (chunk) => {
chunks.push(
chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk),
);
});
instance.on('end', () => {
try {
const blob = new Blob(chunks, { type: 'application/pdf' });
callOnRender({ blob, _INTERNAL__LAYOUT__DATA_ });
resolve(blob);
} catch (error) {
reject(error);
}
});
});
};
// TODO: rename this method to `toStream` in next major release, because it return stream not a buffer
const toBuffer = async () => {
const {
layout: _INTERNAL__LAYOUT__DATA_,
fileStream,
} = await render();
callOnRender({_INTERNAL__LAYOUT__DATA_});
return fileStream;
};
/*
* TODO: remove this method in next major release. it is buggy
* see
* - https://github.com/diegomura/react-pdf/issues/2112
* - https://github.com/diegomura/react-pdf/issues/2095
*/
const toString = async () => {
if (process.env.NODE_ENV === 'development') {
console.warn(
'`toString` is deprecated and will be removed in next major release',
);
}
let result = '';
const { fileStream: instance } = await render(false); // For some reason, when rendering to string if compress=true the document is blank
return new Promise((resolve, reject) => {
try {
instance.on('data', (buffer) => {
result += buffer;
});
instance.on('end', () => {
callOnRender();
resolve(result);
});
} catch (error) {
reject(error);
}
});
};
const on = (event, listener) => {
if (!events[event]) events[event] = [];
events[event].push(listener);
};
const removeListener = (event, listener) => {
if (!events[event]) return;
const idx = events[event].indexOf(listener);
if (idx > -1) events[event].splice(idx, 1);
};
return {
on,
container,
toBlob,
toBuffer,
toString,
removeListener,
updateContainer,
};
};
const Font = fontStore;
const StyleSheet = {
create: (s) => s,
};
export { version, Font, StyleSheet, pdf, createRenderer };