乐趣区

关于react.js:前端生成pdf

最近要实现一个签合同的需要 须要前端实现合同展现、减少签名及公章、生成合同
诉求:不容许用户随便更改复制文案

网上搜到的办法
canvas 生产 pdf 但会呈现页面被截断状况,所以钻研了一下 react-pdf 和 @react-pdf/renderer,很好用~

@react-pdf/renderer
它会提供自带的元素,能够自定义款式,生成你想要的 pdf

pdf 内容

import {Document, Page, Text, StyleSheet, View, Image} from '@react-pdf/renderer';

// 自定义字体
 Font.register({
  family: 'Oswald',
   src: 'https://64a478.ttf'
 });

const styles = StyleSheet.create({
  body: {
    paddingTop: 60,
    paddingLeft: 40,
    paddingRight: 40,
    paddingBottom: 30,
    fontFamily: 'Oswald'
  },
  title: {
    fontSize: 14,
    textAlign: 'center',
    marginBottom: 30,
  },
  subtitle: {
    fontSize: 14,
    textAlign: 'center',
    marginTop: 20,
    marginBottom: 20,
  },
  introduce: {
    fontSize: 12,
    lineHeight: 1.6,
  },
  graph: {
    fontSize: 12,
    lineHeight: 1.6,
    wrap: true,
    fontWeight: 'thin',
    color: 'rgba(0,0,0,.8)',
  },
  graphText: {
    fontWeight: 'bold',
    textDecoration: 'underline',
    color: '#000',
  },
  signImg: {
    width: '80px',
    height: '80px',
    position: 'absolute',
    left: 0,
    top: 0,
    zIndex: 2
  },
  pageNumber: {
    position: 'absolute',
    fontSize: 12,
    bottom: 30,
    left: 0,
    right: 0,
    textAlign: 'center',
    color: 'grey',
  },
});

const Content = (name?: string) => {
  return <Document>
    <Page size="A4" style={styles.body} >
      <Text style={styles.title}>BBBBB</Text>
      <Text style={styles.introduce}>This Agreement.</Text>
      <Text style={styles.subtitle}>AAAAAAa</Text>

      <Text style={styles.graph}>
        <Text style={styles.graphText}> 加粗文案 </Text><Text> 以后文案在一行内 </Text>
      </Text>
      <Image
            style={styles.signImg}
            src="https:xxxxx.jpg"
          />
     {name ? <Text>{name}</Text> : <></>}
      <Text style={styles.pageNumber} render={({pageNumber, totalPages}) => (`${pageNumber} / ${totalPages}`
      )} fixed />
    </Page>
  </Document>
}

export default Content

pdf 展现

  • hooks 形式生成 pdf 连贯和 blob 文件

    import Content from './Content';
    const [instance] = usePDF({document: Content('xiaoming') });
    // instance
    // loading/url/blob/error
  • DOM 渲染
  • iframe 形式展现,iframe 底色无奈更改,对于款式要求较高的不太实用

    import {PDFViewer} from '@react-pdf/renderer';
    import Content from './Content';
    
    const App = () => (
    <PDFViewer>
      <Content />
    </PDFViewer>
    );
    
    ReactDOM.render(<App />, document.getElementById('root'));

    react-pdf
    pdf 链接渲染展现 pdf,解决 @react-pdf/renderer 款式问题,能够通过第一种形式生成的 blob 地址或 url 渲染

    import React, {useState} from "react";
    import {Document, Page, pdfjs} from 'react-pdf';
    // 6 版本会报错,且此办法无奈解决 须要应用 5 版本
    // "Error: Setting up fake worker failed:"Cannot read properties of undefined (reading 'WorkerMessageHandler')".
    pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
    
    const PdfView = ({url, loadSuccess, loadError}: any) => {const [numPages, setNumPages] = useState(null);
    
    function onDocumentLoadSuccess({numPages: nextNumPages}: any) {setNumPages(nextNumPages);
      loadSuccess?.()}
      
      // 页面会渲染 2 层  有一层的文案可被选中  css 款式解决一下就好
      // .react-pdf__Page__textContent {
      //    display: none;
      //  }
    return url ? <Document file={url} onLoadSuccess={onDocumentLoadSuccess} onLoadError={loadError} options={options} >
      {Array.from(new Array(numPages), (el, index) => (<Page className="contract-content-pdf-page" width={800} key={`page_${index + 1}`} pageNumber={index + 1} />
      ))}
    </Document> : <></>
    }
    
    export default PdfView
    

这两个包联合应该能够实现不同场景不同需要的 pdf 文档需要~

退出移动版