📜 ⬆️ ⬇️

jsPDF + canvas: export to PDF of a multipage table in Russian

Generate PDF ... This topic is not new, but sometimes you can encounter some subtleties, eventually becoming on the thorny path of cycling. Today I will tell you how I designed one such bike.

I needed to make report generation in PDF. For several reasons, I decided to do this on the client side. A quick search gave me a choice between jsPDF and pdfmake . Stopped at first. And now more ...


')
First I want to say that although jsPDF is a great thing, the documentation of this project is irresponsible in places,
that a person is mentally unprepared causes a desire to swear obscene language. The Symfony documentation is remembered: you read it, and then you google with the question “how?” (Or you go to the source code).

The first pitfall thrown in my direction by this library was the lack of support for the Russian language (and UTF-8 as a whole, as far as I was able to figure out ).

(pdfmake, on the contrary, can work with UTF-8, but I soon refused to use this library.)

After searching and experimenting, I decided to use canvas for drawing. However, there was a second pitfall here: canvas captures only the current screen and my huge table was saved as a set of empty sheets (well, sparingly).

I had to break the table with a script, put it into a temporary container, make a canvas of it and use a new one. That's what happened in the end:
app.factory("PDF", function() { return { tableToPDF: function() { var pdf = new jsPDF('p', 'pt', 'a4'); var rows = $("table").find("tr"); var pdfContainer = $(".tmp-pdf-container"); var pdfInternals = pdf.internal; var pdfPageSize = pdfInternals.pageSize; var pdfPageWidth = pdfPageSize.width; var pdfPageHeight = pdfPageSize.height; var partialSize = 10; var contentSize = 0; var marginTop = 20; var index = 0; //     partialSize  var generatePartial = function() { var partial = ""; rows.each(function(i, row) { if (i >= index && i <= partialSize) { partial += "<tr>" + $(this).html() + "</tr>"; index++; if (index >= partialSize) { partialSize += 10; return false; } } }); return partial; } var processCanvases = function() { if (index >= rows.length) { pdfContainer.html(""); //pdf.output("datauri"); pdf.save("TEST.pdf"); return; } var partial = generatePartial(); // generate table with that rows var table = $(document.createElement("table")); table.append("<tbody>" + partial + "</tbody>"); // insert table to temporary div pdfContainer.html("<table class='table table-fixed-width table-condensed'>" + table.html() + "</table>"); // hide unnecessary columns pdfContainer.find(".non-printable").css("display", "none"); // generate canvas from that table html2canvas(pdfContainer, {background: "white"}).then(function(canvas) { // contentSize   //   4     partial, //  ,           if (contentSize < 2) { contentSize ++; pdf.addImage(canvas, "jpeg", 20, marginTop, pdfPageWidth-40, 0); //      //   0.01 ,  0.05,   ,     marginTop += canvas.height/ (canvas.width / pdfPageHeight + (pdfPageWidth / pdfPageHeight) - 0.05); } else { pdf.addImage(canvas, "jpeg", 20, marginTop, pdfPageWidth-40, 0); //   ,         if (index < rows.length) { pdf.addPage(); } contentSize = 0; marginTop = 0; } // next iteration processCanvases(); }); } processCanvases(); } } }); 


 <div class="tmp-pdf-container" style="position: absolute; left: -9999; width: 1000px"></div> 


I also want to mention the format of PNG. The thing is good (as far as I know, it is supported by older browsers, unlike image / jpeg), but PDF is heavier. In addition, on large reports, the browser is guaranteed to fall, it was my chrome that threw out the error window, and FF generally put itself and the system, therefore, only the sledge hammer saved.

When I started to generate in JPEG, I got an afro black background. It turned out that JPEG transparency makes afro black .

Also, html2canvas, does not know how to generate a canvas from code, so you need to create some kind of temporary element. Plus, it does not render invisible items. Somewhere on stackoverflow, iframe was advised, I personally added a div with position: absolute and left: -9999, so as not to interfere (though I couldn’t drive the slider out of the screen)

Also, there is a difficulty when cutting the table: the columns are different. I cured it, added the following style:
  .table-fixed-width { table-layout: fixed; } 


Here is an example: plnkr.co/edit/r3BaDwHpK5H9giwpqrBb

Conclusion: writing bikes is not always bad. Often it helps to learn something new. However, this should not become a habit, so you should always read the docks, at least in order to popsy slightly .

I hope that this article will help someone, and also hope to hear your advice and ideas.

Source: https://habr.com/ru/post/278047/


All Articles