demand

After users fill out the form and click save, they can download the PDF directly.

solution

Server generation

Train of thought

Google Chrome developed Chrome Headless in 2007, along with the release of Puppeteer, a browser that can be understood as a browser with no interface but complete server features.

Therefore, you can start the Puppeteer browser on the server, open the target URL, and use the conversion function of Chrome to convert HTML to PDF.

The server generates the core code

Puppeteer must be installed first. The NPM installation may cause errors. It is best to use CNPM Taobao image to install puppeteer.

Enter CNPM I puppeteer -s to install the dependency.

To create a JS file, simply open the url in the Puppeteer browser and save the PDF.

// html2pdf.js

const puppeteer = require('puppeteer');
(async function(){
    // Start the service
    const browser = await puppeteer.launch();
    // Open the TAB
    const page = await browser.newPage();
    // Go to this address
    await page.goto('https://koa.bootcss.com/#context');
    // Convert HTML pages to PDF and save to path
    await page.pdf({path:"test.pdf".format:'A4'})
    // Close the browser
    awaitbrowser.close(); }) ();Copy the code

The console then enters Node html2pdF.js to start the service.

Of course, module.export can also export module methods, according to the business logic.

disadvantages

  • Unable to save form dynamic data

    Since the page is requested from the server, if user input is not saved at the request address, the truncated PDF will be the original unfilled state of the page.

    In other words, he can only convert static pages, because we need a lot of user input, so pass.

The client generates the core code

Train of thought

  • usehtml2canvas, input the DOM node to be converted, and traverse to convert to canvas canvas
  • Convert canvas canvas to base64 image usingjsPDFCreate a PDF file and insert the image into the PDF.

disadvantages

  • Distortion.

    It is obvious that the resolution and configuration of the page may affect the quality of the output image, since it is similar to inserting a screenshot into a PDF.

    At the same time, because it is a screenshot, you may lose page links and other functions.

  • Text truncation

    When canvas canvas is larger than PDF one page, the output will be wrong. At this time, we need to judge whether canvas canvas is larger than A4 size. If so, split canvas and insert it into different pages.

    At this point, the problem arises again. Since the picture is segmented, the picture or text may be truncated in half, because we cannot analyze the structure of the item inside the canvas.

The core code

Our demand without pictures and links, so the distortion of the impact is not big problem for us, at the same time our form is composed of multiple repeat such as long the item, and the item is very short, will not exceed a piece of A4 paper (though this is not serious, if needed, you can get a DOM element is high, wide cut according to the height of DOM elements).

Therefore, I plan to slice canvas directly according to item, and save each item on one A4 page.

There are a few core approaches to understand before you begin:

  • html2canvas
    // DOM is the DOM node to be converted
    html2canvas(DOM,{
        backgroundColor:"#ffffff".width:width,
        height:height,
        scale:2.allowTaint:true,
    }).then((canvas) = >{
        // Canvas is the canvas after the conversion
    })
Copy the code
  • jsPDF
    // Create an instance
    let pdf = new jsPDF(' '.'pt'.'a4');
    // Add the image to the PDF file
    // The first argument is the format of the file to be inserted (base64), and the second is the file format
    // The third and fourth are the coordinates of the upper left corner of the image, and the last two are the width and height of the image after insertion
    pdf.addImage(image,'JPEG'.10.10,height,width);
    // Add a new page
    pdf.addPage()
    // Save the PDF file
    pdf.save()
Copy the code
  • canvas
    // Canvas is the image to be cut
    // sx, sy is the coordinates to start clipping
    // swidth and sHeight are the width and height of the clipping
    // dx and dy are the coordinates of the clipped image inserted into the canvas
    // sWidth,sHeight is the width of the clipped image in the canvas
    cxt.drawImage(canvas,sx,sy,sWidth,sHeight,dx,dy,sWidth,sHeight);
Copy the code
/** * @description: form to PDF file * @return: PDF */
onSubmit(){
    // This is the form I want to convert, there are many same tables in it
    let form = this.$refs.form;
    // Get the width and height of the element
    let width = form.getBoundingClientRect().width;
    let height = form.getBoundingClientRect().height;
    html2canvas(form,{
        backgroundColor:"#ffffff".width:width,
        height:height,
        scale:2.allowTaint:true,
    }).then((canvas) = >{
        let pdf = new jsPDF(' '.'pt'.'a4');
        // Cut the image
        let canvasList = this.splitCanvas(canvas,this.forms.length);

        // Iterate over the Canvas list, adding one image per page
        canvasList.forEach((item,index) = >{
            // Convert the image format to base64
            let itemImage = item.toDataURL('image/jpeg'.1.0);
            // Leave a 10px margin. The width of A4 paper is 595px on a 72-resolution monitor
            pdf.addImage(itemImage,'JPEG'.10.10.575.28.575.28/item.width*item.height);
            // If it is not the last page, it is paged
            index == this.forms.length- 1 ? ' ' : pdf.addPage();
        })
        // Save the file
        let blob = pdf.output('blob');
        
        pdf.save('test.pdf'); })},@param {number} num Number of slices * @param {canvas} canvas * @return {array} canvas list */
splitCanvas(canvas,num){
    let height = canvas.height,width = canvas.width;
    let chunkHeight = height/num;// The height of each slice
    let chunkList = [];// Store the resulting canvas
    for(let i=0; i<height ; i+=chunkHeight){
        // Initialize the clipping rectangle position
        let sx = 0,sy = i,sWidth = width,sHeight = chunkHeight,dx = 0, dy = 0;
        // Create a canvas node
        let canvasItem =document.createElement("canvas");
        // Initialize the canvas size
        canvasItem.height = chunkHeight;
        canvasItem.width = width;
        let cxt = canvasItem.getContext("2d");
        // Place the cropped image on the new canvas node
        cxt.drawImage(canvas,sx,sy,sWidth,sHeight,dx,dy,sWidth,sHeight);
        chunkList.push(canvasItem); 
    }
    return chunkList;
},
Copy the code

The final result

  • The page after the form is saved

  • Conversion to PDF effect