在 ReactJS 中将创建的 PDF 与现有的本地 PDF 合并
Merge created PDF with existing local PDF in ReactJS
我使用 react-pdf/renderer and download it using file-saver 在 ReactJS 中创建了一个 PDF。
这是我创建 PDF
并下载它的代码:
const LazyDownloadPDFButton = (number, date, totalHours, formattedHours) => (
<Button
className={classes.download}
onClick={
async () => {
const doc = <InvoicePDF number={number} date={date} totalHours={totalHours} formattedHours={formattedHours} />
const asPdf = pdf()
asPdf.updateContainer(doc)
const blob = await asPdf.toBlob()
saveAs(blob, `PDF${number}.pdf`)
}}>
Download
</Button>
)
其中 InvoicePDF
是一个单独的组件,它使用必要的参数呈现 PDF
页面,如 react-pdf/renderer
文档页面。
在下载实际的 PDF
之前,我必须将其与另一个将从计算机驱动器中选择的现有 PDF
合并。为此,我有下一个代码片段:
fileRef = useRef()
<Button onClick={() => fileRef.current.click()}>
Upload file
<input
ref={fileRef}
type='file'
style={{ display: 'none' }}
/>
</Button>
哪个returns我的文件的详细信息。
我尝试 updateContainer
使用这个选定的文件,但出现错误。
如何将这个新文件与创建的 InvoicePDF
合并?
与此同时,我尝试从 arrayBuffers
创建最后一个 blob
,如下所示:
这是将 created PDF
与 selected PDF
连接起来的函数,它 returns 是正确的总和。
function concatArrayBuffers(buffer1, buffer2) {
if (!buffer1) {
return buffer2;
} else if (!buffer2) {
return buffer1;
}
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
};
我的方法现在有一个 finalBlob
是用 arrayBuffers
创建的,但是 问题 是结果 PDF
将始终包含只是 content of the second arrayBuffer (which is either the selected pdf or the created pdf)
const LazyDownloadPDFButton = (number, date, totalHours, formattedHours) => (
<Button
className={classes.download}
onClick={
async () => {
const doc = <InvoicePDF number={number} date={date} totalHours={totalHours} formattedHours={formattedHours} />
const asPdf = pdf()
asPdf.updateContainer(doc)
const initialBlob = await new Blob([fileRef.current.files[0]], { type: 'application/pdf' }).arrayBuffer()
const blob = await (await asPdf.toBlob()).arrayBuffer()
const finalArrayBuffer = concatArrayBuffers(initialBlob, blob)
const finalBlob = new Blob([finalArrayBuffer], { type: 'application/pdf' })
saveAs(finalBlob, `PDF${number}.pdf`)
}}
>
Download
</Button>
)
解决方案
经过一些研究和多次失败的尝试后,我得出了一个关于如何以正确的顺序合并 PDFs
和 bonus
添加图像(在我的例子中是签名)的答案在最终 PDF 的每一页上。
这是最终代码:
function base64toBlob(base64Data, contentType) {
contentType = contentType || '';
var sliceSize = 1024;
var byteCharacters = atob(base64Data);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, { type: contentType });
}
async function mergeBetweenPDF(pdfFileList, number) {
const doc = await PDFDocument.create()
const getUserSignature = () => {
switch (selectedUser.id) {
case 1:
return FirstImage
case 2:
return SecondImage
default:
return null
}
}
const pngURL = getUserSignature()
const pngImageBytes = pngURL ? await fetch(pngURL).then((res) => res.arrayBuffer()) : null
const pngImage = pngURL ? await doc.embedPng(pngImageBytes) : null
const pngDims = pngURL ? pngImage.scale(0.5) : null
const initialPDF = await PDFDocument.load(pdfFileList[0])
const appendixPDF = await PDFDocument.load(pdfFileList[1])
const initialPDFPages = await doc.copyPages(initialPDF, initialPDF.getPageIndices())
for (const page of initialPDFPages) {
if (pngURL) {
page.drawImage(pngImage, {
x: page.getWidth() / 2 - pngDims.width / 2 + 75,
y: page.getHeight() / 2 - pngDims.height,
width: pngDims.width,
height: pngDims.height,
});
}
doc.addPage(page)
}
const appendixPDFPages = await doc.copyPages(appendixPDF, appendixPDF.getPageIndices())
for (const page of appendixPDFPages) {
if (pngURL) {
page.drawImage(pngImage, {
x: page.getWidth() / 2 - pngDims.width / 2 + 75,
y: page.getHeight() / 2 - pngDims.height,
width: pngDims.width,
height: pngDims.height,
});
}
doc.addPage(page)
}
const base64 = await doc.saveAsBase64()
const bufferArray = base64toBlob(base64, 'application/pdf')
const blob = new Blob([bufferArray], { type: 'application/pdf' })
saveAs(blob, `Appendix${number}.pdf`)
}
const LazyDownloadPDFButton = (number, date, totalHours, formattedHours) => (
<Button
className={classes.download}
onClick={
async () => {
const doc = <InvoicePDF number={number} date={date} totalHours={totalHours} formattedHours={formattedHours} />
const asPdf = pdf()
asPdf.updateContainer(doc)
let initialBlob = await new Blob([fileRef.current.files[0]], { type: 'application/pdf' }).arrayBuffer()
let appendixBlob = await (await asPdf.toBlob()).arrayBuffer()
mergeBetweenPDF([initialBlob, appendixBlob], number)
}}
>
Download
</Button>
)
所以 LazyDownloadPDFButton
是我的按钮,它请求相应的参数来创建最终的 PDF。 InvoicePDF
是我创建的带有参数的PDF,initialBlob
是我上传到我页面的PDF
,它要求在合并的PDF中是第一个,appendixBlob
是创建的 PDF 将附加到 initialBlob
.
在 mergeBetweenPDF
中,我正在使用 pdf-lib 库创建最终文档,我在其中创建图像,获取发送的 2 个初始 PDF,循环它们,在每一页上添加图像, 然后将每一页添加到将要下载的最终文档中。
希望有一天这会对某人有所帮助。
只是我做的一个简单的解决方案...
https://github.com/ManasMadan/pdf-actions
https://www.npmjs.com/package/pdf-actions
import { createPDF,pdfArrayToBlob, mergePDF } from "pdf-actions";
// Async Function To Merge PDF Files Uploaded Using The Input Tag in HTML
const mergePDFHandler = async (files) => {
// Converting File Object Array To PDF Document Array
files.forEach((file)=>await createPDF.PDFDocumentFromFile(file))
// Merging The PDF Files to A PDFDocument
const mergedPDFDocument = await mergePDF(files)
// Converting The Merged Document to Unit8Array
const mergedPdfFile = await mergedPDFDocument.save();
// Saving The File To Disk
const pdfBlob = pdfArrayToBlob(mergedPdfFile);
};
我使用 react-pdf/renderer and download it using file-saver 在 ReactJS 中创建了一个 PDF。
这是我创建 PDF
并下载它的代码:
const LazyDownloadPDFButton = (number, date, totalHours, formattedHours) => (
<Button
className={classes.download}
onClick={
async () => {
const doc = <InvoicePDF number={number} date={date} totalHours={totalHours} formattedHours={formattedHours} />
const asPdf = pdf()
asPdf.updateContainer(doc)
const blob = await asPdf.toBlob()
saveAs(blob, `PDF${number}.pdf`)
}}>
Download
</Button>
)
其中 InvoicePDF
是一个单独的组件,它使用必要的参数呈现 PDF
页面,如 react-pdf/renderer
文档页面。
在下载实际的 PDF
之前,我必须将其与另一个将从计算机驱动器中选择的现有 PDF
合并。为此,我有下一个代码片段:
fileRef = useRef()
<Button onClick={() => fileRef.current.click()}>
Upload file
<input
ref={fileRef}
type='file'
style={{ display: 'none' }}
/>
</Button>
哪个returns我的文件的详细信息。
我尝试 updateContainer
使用这个选定的文件,但出现错误。
如何将这个新文件与创建的 InvoicePDF
合并?
与此同时,我尝试从 arrayBuffers
创建最后一个 blob
,如下所示:
这是将 created PDF
与 selected PDF
连接起来的函数,它 returns 是正确的总和。
function concatArrayBuffers(buffer1, buffer2) {
if (!buffer1) {
return buffer2;
} else if (!buffer2) {
return buffer1;
}
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
};
我的方法现在有一个 finalBlob
是用 arrayBuffers
创建的,但是 问题 是结果 PDF
将始终包含只是 content of the second arrayBuffer (which is either the selected pdf or the created pdf)
const LazyDownloadPDFButton = (number, date, totalHours, formattedHours) => (
<Button
className={classes.download}
onClick={
async () => {
const doc = <InvoicePDF number={number} date={date} totalHours={totalHours} formattedHours={formattedHours} />
const asPdf = pdf()
asPdf.updateContainer(doc)
const initialBlob = await new Blob([fileRef.current.files[0]], { type: 'application/pdf' }).arrayBuffer()
const blob = await (await asPdf.toBlob()).arrayBuffer()
const finalArrayBuffer = concatArrayBuffers(initialBlob, blob)
const finalBlob = new Blob([finalArrayBuffer], { type: 'application/pdf' })
saveAs(finalBlob, `PDF${number}.pdf`)
}}
>
Download
</Button>
)
解决方案
经过一些研究和多次失败的尝试后,我得出了一个关于如何以正确的顺序合并 PDFs
和 bonus
添加图像(在我的例子中是签名)的答案在最终 PDF 的每一页上。
这是最终代码:
function base64toBlob(base64Data, contentType) {
contentType = contentType || '';
var sliceSize = 1024;
var byteCharacters = atob(base64Data);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, { type: contentType });
}
async function mergeBetweenPDF(pdfFileList, number) {
const doc = await PDFDocument.create()
const getUserSignature = () => {
switch (selectedUser.id) {
case 1:
return FirstImage
case 2:
return SecondImage
default:
return null
}
}
const pngURL = getUserSignature()
const pngImageBytes = pngURL ? await fetch(pngURL).then((res) => res.arrayBuffer()) : null
const pngImage = pngURL ? await doc.embedPng(pngImageBytes) : null
const pngDims = pngURL ? pngImage.scale(0.5) : null
const initialPDF = await PDFDocument.load(pdfFileList[0])
const appendixPDF = await PDFDocument.load(pdfFileList[1])
const initialPDFPages = await doc.copyPages(initialPDF, initialPDF.getPageIndices())
for (const page of initialPDFPages) {
if (pngURL) {
page.drawImage(pngImage, {
x: page.getWidth() / 2 - pngDims.width / 2 + 75,
y: page.getHeight() / 2 - pngDims.height,
width: pngDims.width,
height: pngDims.height,
});
}
doc.addPage(page)
}
const appendixPDFPages = await doc.copyPages(appendixPDF, appendixPDF.getPageIndices())
for (const page of appendixPDFPages) {
if (pngURL) {
page.drawImage(pngImage, {
x: page.getWidth() / 2 - pngDims.width / 2 + 75,
y: page.getHeight() / 2 - pngDims.height,
width: pngDims.width,
height: pngDims.height,
});
}
doc.addPage(page)
}
const base64 = await doc.saveAsBase64()
const bufferArray = base64toBlob(base64, 'application/pdf')
const blob = new Blob([bufferArray], { type: 'application/pdf' })
saveAs(blob, `Appendix${number}.pdf`)
}
const LazyDownloadPDFButton = (number, date, totalHours, formattedHours) => (
<Button
className={classes.download}
onClick={
async () => {
const doc = <InvoicePDF number={number} date={date} totalHours={totalHours} formattedHours={formattedHours} />
const asPdf = pdf()
asPdf.updateContainer(doc)
let initialBlob = await new Blob([fileRef.current.files[0]], { type: 'application/pdf' }).arrayBuffer()
let appendixBlob = await (await asPdf.toBlob()).arrayBuffer()
mergeBetweenPDF([initialBlob, appendixBlob], number)
}}
>
Download
</Button>
)
所以 LazyDownloadPDFButton
是我的按钮,它请求相应的参数来创建最终的 PDF。 InvoicePDF
是我创建的带有参数的PDF,initialBlob
是我上传到我页面的PDF
,它要求在合并的PDF中是第一个,appendixBlob
是创建的 PDF 将附加到 initialBlob
.
在 mergeBetweenPDF
中,我正在使用 pdf-lib 库创建最终文档,我在其中创建图像,获取发送的 2 个初始 PDF,循环它们,在每一页上添加图像, 然后将每一页添加到将要下载的最终文档中。
希望有一天这会对某人有所帮助。
只是我做的一个简单的解决方案...
https://github.com/ManasMadan/pdf-actions https://www.npmjs.com/package/pdf-actions
import { createPDF,pdfArrayToBlob, mergePDF } from "pdf-actions";
// Async Function To Merge PDF Files Uploaded Using The Input Tag in HTML
const mergePDFHandler = async (files) => {
// Converting File Object Array To PDF Document Array
files.forEach((file)=>await createPDF.PDFDocumentFromFile(file))
// Merging The PDF Files to A PDFDocument
const mergedPDFDocument = await mergePDF(files)
// Converting The Merged Document to Unit8Array
const mergedPdfFile = await mergedPDFDocument.save();
// Saving The File To Disk
const pdfBlob = pdfArrayToBlob(mergedPdfFile);
};