preface

PDF was developed 30 years ago, and is one of the most widely used file formats. We like to use it most as a format for resumes, contracts, invoices, e-books, etc. The main reason is that the document format can be compatible with multiple devices and applications, and the content remains 100% the same format.

A brief introduction the React – PDF

React PDF is a tool for creating PDF files on browsers, mobile devices, and servers using React.

You can use CSS properties for styling, flexbox for layout, which supports rendering text, images, SVG, and more. See the website for details

Program implementation

Today I will build an online resume generator using react-PDF and next

Online address: CV. Runjs.cool /

Initialize the project

yarn create next-app --example with-ant-design next-resume 
cd next-resume
yarn add @react-pdf/renderer
Copy the code

React-pdf rendering requires some additional dependencies and webPack5 configuration.

yarn add process browserify-zlib stream-browserify util buffer assert
Copy the code

This step is done because react-PDF is built on top of PDFKit and requires the use of two Node.js API Polyfill when using a browser. While WebPack 5 no longer includes automatic import of NodeJS polyfills, we must choose to enter all polyfills we want. To do this, we must add some dependencies to our project:

Create a next. Config.js file in the root directory

module.exports = {
  reactStrictMode: true.webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) = >{ config.resolve.fallback = { ... config.resolve.fallback,module: "empty".dgram: "empty".dns: "mock".fs: "empty".http2: "empty".net: "empty".tls: "empty".child_process: "empty".process: require.resolve("process/browser"),
      zlib: require.resolve("browserify-zlib"),
      stream: require.resolve("stream-browserify"),
      util: require.resolve("util"),
      buffer: require.resolve("buffer"),
      asset: require.resolve("assert"),}; config.plugins.push(new webpack.ProvidePlugin({
        Buffer: ["buffer"."Buffer"].process: "process/browser",}));returnconfig; }};Copy the code

Implementation logic

New in app.js binds user input to State in real time, and then renders the preview page in real time

import Preview from './component/Preview'
import React, { useState } from 'react'
function App() {
  const [profile, setProfile] = useState({
    name: "Galloping Pony.".about: "Share popular Javascript \n framework, explore the ultimate \n optimization experience of the Web.".email: "[email protected]".avatar:"https://p6-passport.byteacctimg.com/img/user-avatar/585e1491713363bc8f67d06c485e8260~300x300.image",})const handleChange = (name, value) = >{ setProfile({ ... profile, [name]: value }) }return (
    <div
      style={{
        width: '100% ',height: '100vh',
        display: 'flex'}} >
      <div style={{ width: '50'}} % >
        <div>
          <label>The name</label>
          <input
            name='name'
            defaultValue={profile.name}
            onChange={(e)= > {
              handleChange(e.target.name, e.target.value)
            }}
          />
        </div>
        <div>
          <label>Head address</label>
          <input
            name='avatar'
            defaultValue={profile.avatar}
            onChange={(e)= > {
              handleChange(e.target.name, e.target.value)
            }}
          />
        </div>
        <div>
          <label>Introduction to the</label>
          <input
            name='about'
            defaultValue={profile.about}
            onChange={(e)= > {
              handleChange(e.target.name, e.target.value)
            }}
          />
        </div>
        <div>
          <label>email</label>
          <input
            name='email'
            defaultValue={profile.email}
            onChange={(e)= > {
              handleChange(e.target.name, e.target.value)
            }}
          />
        </div>
      </div>
      <Preview profile={profile} />
    </div>)}export default App

Copy the code

Preview.js is the right part of the page and is embedded with the PDF document we will create.

We also have PDFDownloadLink, which you can use to download PDF files.

import React from 'react'
import { Document, Page, PDFViewer, PDFDownloadLink } from '@react-pdf/renderer'
import LeftSection from './LeftSection'
import  RightSection from './RightSection'
import styles from '.. /styles'

const Preview = ({ profile }) = > {
  return (
    <div style={{ flexGrow: 1}} >
      <PDFViewer
        showToolbar={false}
        style={{
          width: '100% ',height: '95% '}} >
        <Template profile={profile} />
      </PDFViewer>
      <PDFDownloadLink
        document={<Template profile={profile} />} fileName='somename.pdf' > {({ loading }) => (loading ? 'Loading document... ' : 'Download now! ')}</PDFDownloadLink>
    </div>)}// Create the document component
const Template = ({ profile }) = > {
  return (
    <Document>
      <Page size='A4' style={styles.page}>
        <LeftSection profile={profile} />
        <RightSection about={profile.about} />
      </Page>
    </Document>)}export default Preview

Copy the code

We can directly set the PDF to A4 paper size.

import { StyleSheet } from '@react-pdf/renderer'

export default StyleSheet.create({
  page: {
    display: 'flex'.flexDirection: 'row',},section_right: {
    margin: 10.padding: 10.paddingTop: 20.width: '75%',},section_left: {
    width: '25%'.height: '100%'.backgroundColor: '#084c41',},profile_container: {
    display: 'flex'.flexDirection: 'column'.alignItems: 'center'.marginTop: '20'.marginBottom: '20px'.height: '150',},name_text: {
    paddingTop: '10px'.paddingBottom: '5px'.fontSize: '14px'.fontWeight: '900'.color: 'white',}})Copy the code

Create a JavaScript StyleSheet with stylesheet.create

Leftsection.js code display

import { View, Text, Image, } from '@react-pdf/renderer'
import styles from '.. /styles'

export const Profile = ({ profile }) = > {
  return (
    <View style={styles.profile_container}>
      <Image style={styles.profile_img} src={profile.avatar} />

      <View
        style={{
          justifyContent: 'center'}} >
        <Text style={styles.name_text}>{profile.name}</Text>
      </View>
      <Text style={styles.profession_text}>{profile.about}</Text>
    </View>)}const LeftSection = ({ profile }) = > {
  return (
    <View style={styles.section_left}>
      <Profile profile={profile} />
    </View>)}export default LeftSection

Copy the code

You can also write inline styles directly to control styles within FDF. However, float is not supported. For details, see the official website

Have a problem

I thought I could finish it in this way, but I didn’t expect that there was a giant pit, which did not support Chinese. Chinese would show garbled characters in THE PDF, so I found the answer through issue

import { StyleSheet, Font } from "@react-pdf/renderer";

Font.register({
  family: "Alibaba-PuHuiTi-Light".src: "/Alibaba-PuHuiTi-Light.ttf"});export const styles = StyleSheet.create({
  page: {
    fontFamily: "Alibaba-PuHuiTi-Light".flexDirection: "row".display: "flex". }})Copy the code

Then you can display the Chinese font. Over here, I downloaded the Aribabap.

refactoring

This is a simplified version of the implementation, and you should at least get the idea from the code examples above. In order to make the entire resume data-rich, I used ANTD to implement a rich form list. Use react Context to manage our data. Here is the directory structure:

├ ─ ─ components │ ├ ─ ─ app │ │ └ ─ ─ index. The TSX │ ├ ─ ─ editor │ │ ├ ─ ─ FormCreator. The TSX │ │ ├ ─ ─ conifg. Js │ │ └ ─ ─ index. The TSX │ ├ ─ ─ icon │ │ └ ─ ─ index. The TSX │ └ ─ ─ the preview │ ├ ─ ─ avatar. The TSX │ ├ ─ ─ awardList. The TSX │ ├ ─ ─ educationList. The TSX │ ├ ─ ─ index. The TSX │ ├ ─ ─ profile. The TSX │ ├ ─ ─ projectList. The TSX │ ├ ─ ─ skillList. The TSX │ ├ ─ ─ style.css. Ts │ └ ─ ─ workExpList. The TSX ├ ─ ─ the context │ └ ─ ─ ResumeContext. Ts ├ ─ ─ hooks │ └ ─ ─ useResume │ └ ─ ─ but ts ├ ─ ─ pages │ ├ ─ ─ _app. The TSX │ ├ ─ ─ API │ │ └ ─ ─ hello. Js │ └ ─ ─ Index.tsx ├─ styles ├─ logo.png ├─ ├.cssCopy the code

The deployment of

Finally I used Vercel to deploy and bind custom domain names

Cool/cv.runjs.cool/

That’s all the content of this article. I hope this article is helpful to you. You can also refer to my previous articles or share your thoughts and experiences in the comments section.