Requirements:

In response to the needs of the majority of backend colleagues, download PDF function in the front-end implementation. Later, when I was thinking about how to achieve the goal, I wanted to simply reach the goal by separating the front and back ends and using them cross-platform. As long as the back end provides the interface, the front end does all the rest. Of course, there are some issues, such as security, but the good news is that companies are implementing things that are used internally. So I rolled up my sleeves and got to work.

I. Product demand

  1. Table is cut, another page.
  2. Each generated PDF page is not overwritten by the header footer.
  3. If a table is more than one PDF page, the tr across the row is another page

Two, early stage technology selection

3. Plan determination

  1. Front end separation

  2. The front end realizes the preview download PDF function, and the back end provides the interface

  3. Independently deployed, independent of any existing project. Available for cross-platform projects

Fourth, front-end plug-in

  1. Html2canvas uses JavaScript screen capture
  2. JSPDF is a library that generates PDF using JavaScript
  3. The jsrender template library, with its code-free markup syntax and high performance, relies neither on jQuery nor documentobjectmodel (DOM), supports the creation of custom functions, and uses pure string-based rendering.

Html2canvas + JSPDF

The plugin itself is short on many issues, a large number of data access has been solved. There are also curvilinear solutions (text images are segmented, and the padding is added and removed dynamically by calculation).

1. Download PDF background black (expected: #FFFFFF)

Html2canvas configuration adds background: #FFFFFF

var element = $("#demo");
html2canvas(element,{background :'#FFFFFF'}).then(function(canvas) {
Copy the code

2. Pixel distortion blur

The JS file JSPDF containing these two configurations is introduced

var ele = $("#demo");
html2canvas(ele,{background :'#FFFFFF', scale:2, dpi: 150}).then(function(canvas){})
Copy the code

3. The watermark

Text watermarking

function addWaterMark(pdf) {
  var totalPages = pdf.internal.getNumberOfPages();

  for (i = 1; i <= totalPages; i++) {
    pdf.setPage(i);
    pdf.setTextColor(150);
    pdf.text(50, doc.internal.pageSize.height - 30, 'Watermark');
  }
  return pdf;
}
Copy the code

Image watermarking

/ / add picture watermark var totalPages = PDF. The internal. GetNumberOfPages (); var img = new Image; img.crossOrigin = ""; // for demo as we are at different origin than image img.src = './logo-img1.png'; // some random imgur image img.onload = function() { for (i = 1; i <= totalPages; i++) { pdf.setPage(i); pdf.addImage(img, 520, 0); } pdf.save('order.pdf'); };Copy the code

4. Header footer (same as watermark text)

5. Page text, table, img were cut

Design ideas for problem solving :(strong relevance to business)

Compute element: tr

Calculation method :(here we default the minimum unit tr height not to exceed PDF page height)

  • If tr height exceeds PDF page height, start another page first. And then we go down to tr in smaller units and we recurse.
  • The tr itself is no higher than PDF page height. But just across the page or to the bottom of the current page is not enough to show the footer. It needs another page.

Calculate the finish: Clear the padding of the preview after downloading

Technical implementation:

Both cases require pagination (all TDS under current tr add padding-top)

  • Tr height > PDF height
  • < PDF height, < PDF height, < PDF height, < PDF height, < PDF height >

Header footer and watermark do not affect table cross-page problem: the header footer made two white background width: 100% images added to PDF

Async /await dom operation and download: because there is recursive loop operation of DOM and waiting for DOM operation (TT) before downloading PDF, async/await is perfect solution

Note: Don’t add any padding-top to TD when encoding HTML, save for later download of PDF. To add padding to TD, style a parent element of the td element package.

Html2canvas cannot draw pictures

The IMG element addresses cross domains and allows you to add images to local access.

/ / don't show < img SRC = "https:baidu.com/public/logo.png" > / / show < img SRC = ".. /public/logo.png">Copy the code

Jsrender JS template engine

In the react and Vue era. The early template engines were largely useless. The purpose of this project is to achieve cross-platform front-end and back-end separation. So enable jsRender which is still relatively active on Github to implement.

Structure of 1.

<div class="content-body"></div> <script type="text/x-jsrender" id="j-parent"> <table border="0" cellspacing="0" cellpadding="0" id="pdf-content"> <tbody> <tr> <td>123</td> </tr> </tbody> </table> </script> <script src=".. / jquery - 3.5.1 track of. Min. Js "> < / script > < script SRC =".. /jsrender.min.js"></script> <script type="text/javascript"> const data = { date: '20210127', firstName: 'yang', lastName: 'yd', number: 27, cat: '<span>mimi<span>', skill: [' javascript 'and' HTML ', 'vue'],} / / access template jsRenderTpl = $. Templates (' # j - order - PDF). FinalTpl = jsRenderTpl(data); document.getElementsByClassName('content-body')[0].innerHTML = finalTpl </script>Copy the code

2. Basic usage

{{that}} and {{>}} or {{HTML:}} both can output the content, but the latter is a HTML coding.

<p>a cat {{:cat}}</p> // a mimi
<p>a cat {{>cat}}</p> // a <span>mimi</span>
<p>a cat {{html:cat}}</p> // a <span>mimi</span>
Copy the code

3. Determine the if/else

Syntax: {{if condition}}… {else condition} … {{else}}… {{/if}}

<p>
    {{if age < 6}}
        baby
    {{else age > 50}}
        old man
    {{else}}
        youth
    {{/if}}
<p>
// youth
Copy the code

For 4 cycles

Syntax: {{for}}… {{/for}}

<p>
    {{for skill itemVar="~item"}}
    <span>{{:~item}}</span>
    {{/for}}
</p>
// javascript html vue
Copy the code

Where ~ item is the alias of the value of the current loop.

5. Templates: include combination templates

Syntax: include TMPL =” template id”

<script type="text/x-jsrender" id="j-parent"> {{include {"item": </script> <script type="text/x-jsrender" id="j-child-template"> <p> I am included </p> </script> // I am includedCopy the code

6. views.helpers

Grammar:

  • View {{~” Label name “(additional parameter)}}

  • $.views.helpers({” tag “:function(parameter){code}})

7. Views. Converters

Grammar:

  • View {{” converter Name “: parameter}}

  • Js.views.converters ({” converters “:function(parameter){code}})

    name: {{:~replaceName(firstName, lastName)}}

Seven, safety

When the project moved to the front end implementation, it basically gave up on security… ^_^, because it is used internally, so the front end implements this function. Adding encryption to each ID on the route at least prevents access to the data by modifying the link.

Conclusion:

Since the back end is responsible for downloading PDF this piece. Backend colleagues suffer every time they change styles. This time is also to solve the back-end of the three thousand troubles made a big reform. Implemented cross-platform, any company related to download PDF projects can use this set of implementation.

Technology selection is important at the beginning. All roads lead to Rome. Maybe there’s a better way to do it.