将组件反应为 PDF
React Component To PDF
我正在开发一项功能,允许用户完成管道的最后阶段并从中获得漂亮的 PDF。
我的服务器和客户端都运行良好。我无法完成的是将整个文档转换为 pdf。它只捕获了一小部分。
我不需要多个页面,尽管那样会很好。无论渲染多长时间,我只想捕获所有内容。有可能有 100 个项目,例如图像和旁边的复选框。我需要它的完整 pdf,但到目前为止我只能得到其中一小部分的快照。
我试过搞乱高度选项和各种包,但绝对没有运气。
我将提供 renderSimpleForm 在浏览器上最终的样子。以及它以 PDF 形式出现的内容。
代码和图片
import { jsPDF } from "jspdf";
import * as htmlToImage from 'html-to-image';
const handleSubmit = () => {
// //Before sending the action show in progress text and disable button
setPdf({inProgress: true})
//Creating the pdf blob
htmlToImage.toPng(document.getElementById('simpleForm'))
.then(function (dataUrl) {
var link = document.createElement('a');
link.download = 'my-image-name.jpeg';
const pdf = new jsPDF('p', 'mm', 'a4');
const imgProps= pdf.getImageProperties(dataUrl);
pdf.addImage(dataUrl, 'PNG', 0, 0);
//Send action to server
ClassInstancesActions.StorePassportPDFToDisk(pdf.output('blob'), productID)
});
}
return (
<div id="simpleForm" style={{ height: '9999px !important', width: '9999px !important' }}>
{renderSimpleForm()}
{showError()}
{renderSubmitCancel(disableSubmit)}
</div>
);
};
这是我的 SUMIT 按钮的输出
这是它在浏览器上的样子
好的,一段时间后我有了一个满足我需求的解决方案:
- 指向 React 组件中的 div,并将其 pdf
- 保留样式
和额外的:
- 能够以编程方式自定义和控制数据库中的元数据
- 定制是添加页眉、页脚、页码、首页,技术上我可以控制每一页
我用了https://www.npmjs.com/package/@progress/kendo-react-pdf
我使用了它的 savePDF 函数,它允许我也有一个模板创建类型的功能,因为它是免费的,并且作为 prop 传递到您将在代码中看到的选项参数中。
还有一个 css 选择器仅针对已通过的 div PDF,无需指定要显示的内容和不显示的内容,因此您实际上可以正确操作 PDF在它完全从这里出来之前。
在视频演示中,您会看到我有一个巨大的 div 超过 40 页!
保留了我的 material 和 css 样式,我可以随意添加任何我想要的东西。
我什至添加了水印!
希望这对人们有所帮助!
//component that has the div i need pdf'ed
//
var reactToPdfUtils = require('../../../../../reactToPdf.js');
const handleSave = (sourceElement) => {
console.log('handleSave in SFV!');
reactToPdfUtils.useSavePDFNOW(pdfProps, cb, sourceElement)
function cb(sendDataContent){
console.log(sendDataContent)
}
};
//////////////////////////////////////////////////////////////////////////////////////////////
//reactToPdf.js file that houses the library function I manipulate to get the result
import React, { useEffect } from 'react';
import { savePDF } from '@progress/kendo-react-pdf';
import { drawDOM, exportPDF } from '@progress/kendo-drawing';
export const useSavePDFNOW = (pdfProps, cb, sourceElement) => {
//pdfprops i pass in from meta data in my mongoDB,
//I made a cb to let me know when the funtion is done
//I also do elaborate checking here myself for example
//to make sure there is only one div that that id
//create a template
//this function is declared then passed in later to savePDF,
//the magic in the library allows you to manipulate each page if you wanted. You can do some //cool stuff here.
try {
const onEveryPage = (props) => {
if (hasAfirstPage) {
if (props.pageNum === 1) {
document.querySelectorAll('#toPDF')[0].style.cssText = 'margin-top: -188.333px;height:100%;';
// document.querySelectorAll('kendo-pdf-page')[0].style.cssText = '';
return <Fragment></Fragment>;
} else {
return (
<div style={{ zIndex: '-999999', justifyContent: 'center', display: 'flex' }}>
<div id='header' style={{ textAlign: 'center', fontSize: headerFontToUse + 'px', backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: headerHeightToUse, width: headerWidthToUse, backgroundImage: `url(${headerSrcToUse})`, position: 'absolute', top: 10 }}>
{headerTextToUse}
</div>
<div id='watermarkImage' style={{ backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: waterMarkHeightToUse, width: headerWidthToUse, opacity: 0.5 /* Firefox, Chrome, Safari, Opera, IE >= 9 (preview) */, backgroundImage: `url(${waterMarkSrcToUse})`, position: 'absolute', bottom: -90, left: 300 }}></div>
<div id='pageNums' style={{ position: 'absolute', bottom: 0, right: '10px' }}>
Page {props.pageNum} of {props.totalPages}
</div>
<h6 id='footer' style={{ fontSize: footerFontToUse + 'px', backgroundColor: 'transparent', position: 'absolute', bottom: 0, margin: '6 auto' }}>
{footerTextToUse}
</h6>
</div>
);
}
} else {
return (
<div style={{ zIndex: '-999999', justifyContent: 'center', display: 'flex' }}>
<div id='header' style={{ textAlign: 'center', fontSize: headerFontToUse + 'px', backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: headerHeightToUse, width: headerWidthToUse, backgroundImage: `url(${headerSrcToUse})`, position: 'absolute', top: 10 }}>
{headerTextToUse}
</div>
<div id='watermarkImage' style={{ backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: waterMarkHeightToUse, width: headerWidthToUse, opacity: 0.5 /* Firefox, Chrome, Safari, Opera, IE >= 9 (preview) */, backgroundImage: `url(${waterMarkSrcToUse})`, position: 'absolute', bottom: -90, left: 300 }}></div>
<div id='pageNums' style={{ position: 'absolute', bottom: 0, right: '10px' }}>
Page {props.pageNum} of {props.totalPages}
</div>
<h6 id='footer' style={{ fontSize: footerFontToUse + 'px', backgroundColor: 'transparent', position: 'absolute', bottom: 0, margin: '6 auto' }}>
{footerTextToUse}
</h6>
</div>
);
}
};
//MaterialUI font fixing may have to do this elsewhere too specific
parentWrapper.querySelectorAll('[class*=MuiTypography], [class*=Text], [class*=formControl]').forEach(function (el, i) {
el.setAttribute('style', 'color: black; overflow: visible');
});
//save pdf on client side, send blob to dev to figure out what he or she wants to do with it
let pdfBlob;
savePDF(
parentWrapper,
{
pageTemplate: onEveryPage,
paperSize: 'a4',
fileName: 'Testing',
keepTogether: '.menu',
scale: 0.6,
title: 'Ametek/Powervar Passport',
margin: { top: marginTopToUse, bottom: marginBottomToUse }
},
() => {
// Server side rendering tested comes out exactly the same so long as the props match
drawDOM(parentWrapper, { pageTemplate: onEveryPage, scale: 0.6, paperSize: 'A4', margin: { top: 180, bottom: 10 } })
.then((group) => {
return exportPDF(group);
})
.then((dataUri) => {
// Send action to database
pdfBlob = dataUri.split(';base64,')[1];
ClassInstancesActions.StorePassportPDFToDisk(pdfBlob, 'testing');
cb(pdfBlob);
parentWrapper.remove();
});
}
);
} catch (error) {
console.log(error);
}
};
//This is in a style sheet
//i have a div within the div I pdf with an id of #divToDisableInteraction
//As long as there in there you can do any css magic to your pdf document here
//I tested this to high heaven you can get most things done here
//including adding an image url
kendo-pdf-document #divToDisableInteraction {
visibility: hidden;
}
我正在开发一项功能,允许用户完成管道的最后阶段并从中获得漂亮的 PDF。
我的服务器和客户端都运行良好。我无法完成的是将整个文档转换为 pdf。它只捕获了一小部分。
我不需要多个页面,尽管那样会很好。无论渲染多长时间,我只想捕获所有内容。有可能有 100 个项目,例如图像和旁边的复选框。我需要它的完整 pdf,但到目前为止我只能得到其中一小部分的快照。
我试过搞乱高度选项和各种包,但绝对没有运气。
我将提供 renderSimpleForm 在浏览器上最终的样子。以及它以 PDF 形式出现的内容。
代码和图片
import { jsPDF } from "jspdf";
import * as htmlToImage from 'html-to-image';
const handleSubmit = () => {
// //Before sending the action show in progress text and disable button
setPdf({inProgress: true})
//Creating the pdf blob
htmlToImage.toPng(document.getElementById('simpleForm'))
.then(function (dataUrl) {
var link = document.createElement('a');
link.download = 'my-image-name.jpeg';
const pdf = new jsPDF('p', 'mm', 'a4');
const imgProps= pdf.getImageProperties(dataUrl);
pdf.addImage(dataUrl, 'PNG', 0, 0);
//Send action to server
ClassInstancesActions.StorePassportPDFToDisk(pdf.output('blob'), productID)
});
}
return (
<div id="simpleForm" style={{ height: '9999px !important', width: '9999px !important' }}>
{renderSimpleForm()}
{showError()}
{renderSubmitCancel(disableSubmit)}
</div>
);
};
好的,一段时间后我有了一个满足我需求的解决方案:
- 指向 React 组件中的 div,并将其 pdf
- 保留样式
和额外的:
- 能够以编程方式自定义和控制数据库中的元数据
- 定制是添加页眉、页脚、页码、首页,技术上我可以控制每一页
我用了https://www.npmjs.com/package/@progress/kendo-react-pdf
我使用了它的 savePDF 函数,它允许我也有一个模板创建类型的功能,因为它是免费的,并且作为 prop 传递到您将在代码中看到的选项参数中。
还有一个 css 选择器仅针对已通过的 div PDF,无需指定要显示的内容和不显示的内容,因此您实际上可以正确操作 PDF在它完全从这里出来之前。
在视频演示中,您会看到我有一个巨大的 div 超过 40 页! 保留了我的 material 和 css 样式,我可以随意添加任何我想要的东西。 我什至添加了水印!
希望这对人们有所帮助!
//component that has the div i need pdf'ed
//
var reactToPdfUtils = require('../../../../../reactToPdf.js');
const handleSave = (sourceElement) => {
console.log('handleSave in SFV!');
reactToPdfUtils.useSavePDFNOW(pdfProps, cb, sourceElement)
function cb(sendDataContent){
console.log(sendDataContent)
}
};
//////////////////////////////////////////////////////////////////////////////////////////////
//reactToPdf.js file that houses the library function I manipulate to get the result
import React, { useEffect } from 'react';
import { savePDF } from '@progress/kendo-react-pdf';
import { drawDOM, exportPDF } from '@progress/kendo-drawing';
export const useSavePDFNOW = (pdfProps, cb, sourceElement) => {
//pdfprops i pass in from meta data in my mongoDB,
//I made a cb to let me know when the funtion is done
//I also do elaborate checking here myself for example
//to make sure there is only one div that that id
//create a template
//this function is declared then passed in later to savePDF,
//the magic in the library allows you to manipulate each page if you wanted. You can do some //cool stuff here.
try {
const onEveryPage = (props) => {
if (hasAfirstPage) {
if (props.pageNum === 1) {
document.querySelectorAll('#toPDF')[0].style.cssText = 'margin-top: -188.333px;height:100%;';
// document.querySelectorAll('kendo-pdf-page')[0].style.cssText = '';
return <Fragment></Fragment>;
} else {
return (
<div style={{ zIndex: '-999999', justifyContent: 'center', display: 'flex' }}>
<div id='header' style={{ textAlign: 'center', fontSize: headerFontToUse + 'px', backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: headerHeightToUse, width: headerWidthToUse, backgroundImage: `url(${headerSrcToUse})`, position: 'absolute', top: 10 }}>
{headerTextToUse}
</div>
<div id='watermarkImage' style={{ backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: waterMarkHeightToUse, width: headerWidthToUse, opacity: 0.5 /* Firefox, Chrome, Safari, Opera, IE >= 9 (preview) */, backgroundImage: `url(${waterMarkSrcToUse})`, position: 'absolute', bottom: -90, left: 300 }}></div>
<div id='pageNums' style={{ position: 'absolute', bottom: 0, right: '10px' }}>
Page {props.pageNum} of {props.totalPages}
</div>
<h6 id='footer' style={{ fontSize: footerFontToUse + 'px', backgroundColor: 'transparent', position: 'absolute', bottom: 0, margin: '6 auto' }}>
{footerTextToUse}
</h6>
</div>
);
}
} else {
return (
<div style={{ zIndex: '-999999', justifyContent: 'center', display: 'flex' }}>
<div id='header' style={{ textAlign: 'center', fontSize: headerFontToUse + 'px', backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: headerHeightToUse, width: headerWidthToUse, backgroundImage: `url(${headerSrcToUse})`, position: 'absolute', top: 10 }}>
{headerTextToUse}
</div>
<div id='watermarkImage' style={{ backgroundRepeat: 'no-repeat', backgroundSize: '100%', backgroundColor: 'transparent', height: waterMarkHeightToUse, width: headerWidthToUse, opacity: 0.5 /* Firefox, Chrome, Safari, Opera, IE >= 9 (preview) */, backgroundImage: `url(${waterMarkSrcToUse})`, position: 'absolute', bottom: -90, left: 300 }}></div>
<div id='pageNums' style={{ position: 'absolute', bottom: 0, right: '10px' }}>
Page {props.pageNum} of {props.totalPages}
</div>
<h6 id='footer' style={{ fontSize: footerFontToUse + 'px', backgroundColor: 'transparent', position: 'absolute', bottom: 0, margin: '6 auto' }}>
{footerTextToUse}
</h6>
</div>
);
}
};
//MaterialUI font fixing may have to do this elsewhere too specific
parentWrapper.querySelectorAll('[class*=MuiTypography], [class*=Text], [class*=formControl]').forEach(function (el, i) {
el.setAttribute('style', 'color: black; overflow: visible');
});
//save pdf on client side, send blob to dev to figure out what he or she wants to do with it
let pdfBlob;
savePDF(
parentWrapper,
{
pageTemplate: onEveryPage,
paperSize: 'a4',
fileName: 'Testing',
keepTogether: '.menu',
scale: 0.6,
title: 'Ametek/Powervar Passport',
margin: { top: marginTopToUse, bottom: marginBottomToUse }
},
() => {
// Server side rendering tested comes out exactly the same so long as the props match
drawDOM(parentWrapper, { pageTemplate: onEveryPage, scale: 0.6, paperSize: 'A4', margin: { top: 180, bottom: 10 } })
.then((group) => {
return exportPDF(group);
})
.then((dataUri) => {
// Send action to database
pdfBlob = dataUri.split(';base64,')[1];
ClassInstancesActions.StorePassportPDFToDisk(pdfBlob, 'testing');
cb(pdfBlob);
parentWrapper.remove();
});
}
);
} catch (error) {
console.log(error);
}
};
//This is in a style sheet
//i have a div within the div I pdf with an id of #divToDisableInteraction
//As long as there in there you can do any css magic to your pdf document here
//I tested this to high heaven you can get most things done here
//including adding an image url
kendo-pdf-document #divToDisableInteraction {
visibility: hidden;
}