This is a migrated thread and some comments may be shown as answers.

Page locks upon export to PDF

8 Answers 352 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Lee
Top achievements
Rank 1
Iron
Iron
Iron
Lee asked on 04 Dec 2019, 10:47 PM

When I try to export a file to PDF the browser window locks up while the pdf file is generated. In my case, I am exporting a grid with 124 rows which takes about 5 seconds and another grid with 760 rows which takes about 20 seconds. Besides locking up the page from further scrolling, this is noticeable because the user expands a menu to select the download button and the menu gets stuck open until the file is generated. With this happening I'm also not sure how I would add a loading spinner to the page to tell the user something really is happening.

Any suggestions on how to make it faster and how to close the menu and add a loading spinner would be appreciated.

8 Answers, 1 is accepted

Sort by
0
Alex Hajigeorgieva
Telerik team
answered on 06 Dec 2019, 02:22 PM

Hello, Lee,

I already wrote to you in the other forum thread where I mentioned about the importance of including the Pako Deflate library:

<!-- Load Pako Deflate library to enable PDF compression -->
<script src="https://kendo.cdn.telerik.com/2019.3.1023/js/pako_deflate.min.js"></script>

I also tested a grid with 830 records and exporting all those pages takes 12-13 seconds for 42 pages. The Kendo UI Grid already shows a progress bar while exporting so the users are aware that something is going on.

In my test Dojo I also included a time and a completion counter in percentages by using the pdfExport event. You can check the example here:

https://dojo.telerik.com/@bubblemaster/uZuKoZoc

Kind Regards,
Alex Hajigeorgieva
Progress Telerik

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Lee
Top achievements
Rank 1
Iron
Iron
Iron
answered on 06 Dec 2019, 03:24 PM

Unfortunately the built in progress bar doesn't show because my grid is very long and so it ends up somewhere below the bottom of the screen. I'm more concerned about the length of time and the lockup that happens though. I noticed even in your example it takes several seconds for the counter to start running but at least yours is down to 12 seconds. 

I do have the pako.min.js library at the top of the page. I just changed it to point to the URL mentioned above instead of my local copy but it didn't change the results. When I remove it completely though I get a "download failed - network error" after about a minute so I am confident it is doing something. 

My grid is setup like below. The data comes from an API and the page initially loads completely in about 4 seconds. The excel export happens almost instantly but the pdf is still taking about 33 seconds. I have 7 columns. 4 are displayed on the screen as is and 3 have the dollar sign added and the numbers rounded to 2 decimal places. Would you happen to have any other ideas as to why mine is taking twice as long as yours?

$("#impactTable").kendoGrid({
        dataSource: {
            data: data
        },
        excel: {
            fileName: "Impact Table.xlsx",
            filterable: true
        },
        pdf: {
            fileName: "Key Item Impact.pdf",
            paperSize: "Letter",
            landscape: true,
            scale: 0.7,
            margin: { top: "0.5in", left: "0.25in", right: "0.25in", bottom: "0.5in" },
            title: "Key Item Impact",
            multiPage: true,
            repeatHeaders: true,
            avoidLinks: true,
            template: $("#keyItemImpactPDFTemplate").html()
        },
        rowTemplate: kendo.template($("#keyItemRowTemplate").html()),
        columns: keyItemImpactColumns,
        sortable: true,
        toolbar: [
            { template: standardGridToolbar("keyItemImpact") }
        ],
        search: {
            fields: ["catName", "itemID", "itemName", "storeCount"]
        },
        dataBound: function (e) {
            $('#itemImpactTableSpinner').removeClass('show');
            $('#impactTable .dropdown-trigger').dropdown();
        }
    });

 

 

0
Lee
Top achievements
Rank 1
Iron
Iron
Iron
answered on 06 Dec 2019, 07:24 PM

After digging into it a little deeper I discovered the difference between my usage and the example above is that I have 2 more columns, a row template, and page formatting. I extended the example dojo here to be similar to my actual project code and now it also takes over 30 seconds to render. I also noticed that the timer doesn't start counting until after about 22 seconds during which time the dojo screen is locked.

001.<!DOCTYPE html>
002.<html>
003.  <head>
005.    <style>html { font-size: 14px; font-family: Arial, Helvetica, sans-serif; }</style>
006.    <title></title>
008. 
011. 
012.<!-- Load Pako Deflate library to enable PDF compression -->
014.  </head>
015.  <body>
016.<style>
017.  #time {
018.    background-color:gray;
019.    color:white;
020.    font-size: 1.5em;
021.  }
022.    </style>
023.    <div id="example">
024.      <div id="time"></div>
025.      <div id="progress-status"></div>
026.      <div id="grid"></div>
027.       
028.      <script type="text/x-kendo-tmpl" id="keyItemImpactPDFTemplate">
029.    #
030.    var theDate = new Date();
031.    #
032.    <div class="page-template">
033.        <div class="header">
034.            Key Item Impact Report
035.        </div>
036.        <div class="footer">
037.            <span>#=theDate.toLocaleDateString("en-US")#</span>
038.            <div style="float: right">Page #:pageNum# of #:totalPages#</div>
039.        </div>
040.    </div>
041.      </script>
042.     
043.<script id="keyItemRowTemplate" type="text/x-kendo-tmpl">
044.    <tr class="k-master-row" data-uid="#: OrderID #">
045.        <td class="js-category">
046.            #: OrderID #
047.        </td>
048.        <td class="js-item-number">
049.            #: Freight #
050.        </td>
051.        <td class="js-item-name">
052.           #: OrderDate #
053.        </td>
054.        <td class="js-average-old-price">
055.           #: ShipName #
056.        </td>
057.        <td class="js-average-new-price">
058.             #: ShipCity #
059.        </td>
060.        <td class="js-required-date">
061.            #: RequiredDate #
062.        </td>
063.        <td class="js-emloyee-id">
064.         #: EmployeeID #
065.        </td>
066.    </tr>
067.</script>
068.       
069.      <script>
070.        var start;
071.        $(document).ready(function() {
072.          $("#grid").kendoGrid({
073.            toolbar:["pdf"],
074.            pdf:{
075.                allPages:true,
076.              fileName: "test.pdf",
077.              paperSize: "Letter",
078.              landscape: true,
079.              scale: 0.7,
080.              margin: { top: "0.5in", left: "0.25in", right: "0.25in", bottom: "0.5in" },
081.              title: "Key Item Impact",
082.              multiPage: true,
083.              repeatHeaders: true,
084.              avoidLinks: true
085.            },
086.            pdfExport: function(e) {
087.              start = new Date();
088.              e.promise
089.                .progress(function(e) {
090.                $("#progress-status").text(kendo.format("{0:P} complete", e.progress));
091.                $("#time").text(kendo.format("Time elapsed - {0} seconds", (new Date() - start)/1000));
092.              })
093.                .done(function() {
094.                $("#progress-status").hide();
095.              });
096.            },
097.            rowTemplate: kendo.template($("#keyItemRowTemplate").html()),
098.            dataSource: {
099.              type: "odata",
100.              transport: {
101.                read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Orders"
102.              },
103.              schema: {
104.                model: {
105.                  fields: {
106.                    OrderID: { type: "number" },
107.                    Freight: { type: "number" },
108.                    ShipName: { type: "string" },
109.                    OrderDate: { type: "date" },
110.                    ShipCity: { type: "string" },
111.                    RequiredDate: { type: "date" },
112.                    EmployeeID: { type: "number" }
113.                  }
114.                }
115.              }
116.            },
117.            sortable: true,
118.             
119.            columns: [{
120.              field:"OrderID",
121.              filterable: false
122.            },
123.                      "Freight",
124.                      {
125.                        field: "OrderDate",
126.                        title: "Order Date",
127.                        format: "{0:MM/dd/yyyy}"
128.                      }, {
129.                        field: "ShipName",
130.                        title: "Ship Name"
131.                      }, {
132.                        field: "ShipCity",
133.                        title: "Ship City"
134.                      }, {
135.                        field: "RequiredDate",
136.                        title: "Required Date",
137.                        format: "{0:MM/dd/yyyy}"
138.                      }, {
139.                        field: "EmployeeID",
140.                        title: "Employee ID"
141.                      }
142.                     ]
143.          });
144.        });
145.      </script>
146.    </div>
147. 
148. 
149.  </body>
150.</html>

 

 

0
Alex Hajigeorgieva
Telerik team
answered on 10 Dec 2019, 02:46 PM

Hello, Lee,

Thank you very much for the provided example.

The reason for the behaviour is the height of the rows which creates 136 pages exported for 26 seconds as opposed to 56 pages exported for 12 seconds. The export speed is the same averaging at 0.2 seconds per page (on my machine).

So if you still use a row template but format the dates where the extra 80 pages come from, the speed is the same on my machine with or without a rowTemplate:

https://dojo.telerik.com/iposOqOG

<script id="keyItemRowTemplate" type="text/x-kendo-tmpl">
    <tr data-uid="#: uid  #">
        <td>
            #: OrderID #
        </td>
        <td>
            #: Freight #
        </td>
        <td>
           #: kendo.toString(OrderDate, 'MM/dd/yyyy') #
        </td>
        <td>
           #: ShipName #
        </td>
        <td>
        	 #: ShipCity #
        </td>
        <td>
        	#: kendo.toString(RequiredDate,'MM/dd/yyyy') #
        </td>
        <td>
         #: EmployeeID #
        </td>
    </tr>
</script>

How many pages does the grid in your project generate for 760 rows and how many columns are present?

Kind Regards,
Alex Hajigeorgieva
Progress Telerik

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Lee
Top achievements
Rank 1
Iron
Iron
Iron
answered on 10 Dec 2019, 03:37 PM
It is generating 73 pages in my project.
0
Alex Hajigeorgieva
Telerik team
answered on 12 Dec 2019, 10:11 AM

Hello, Lee,

So in theory it should take 14 seconds approximately if all else is the same as the mocked example which also has seven columns and even more records than the grid in the project you are working on.

This leads me to believe that reason might be in the speed of the response because if serverPaging is used then the PDF export will make a request for the data which is not present on the client. It also clones the exiting HTM and the Excel export just uses the underlying data.

Do you think you can provide us with a runnable example or as a minimum the complete grid and data source setup right now? In case of server operations, is the model as simple as needed or does it carry data that does not get used in the grid?

Kind Regards,
Alex Hajigeorgieva
Progress Telerik

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Lee
Top achievements
Rank 1
Iron
Iron
Iron
answered on 12 Dec 2019, 01:49 PM
I cannot provide a runnable example since my data source is not publicly available. This is the grid code though. The only unused data that the source has is a field called "name" which is not needed for this table. 
<script id="myRowTemplate" type="text/x-kendo-tmpl">
    <tr class="k-master-row" data-uid="#: itemID #">
        <td class="js-category">
            #: category #
        </td>
        <td class="js-item-number">
            #: itemNumber #
        </td>
        <td class="js-item-name">
            #: itemName #
        </td>
        <td class="js-price1">
            #=kendo.format("{0:c}", Price1)#
        </td>
        <td class="js-price2">
            #=kendo.format("{0:c}", Price2)#
        </td>
        <td class="js-price3">
            #=kendo.format("{0:c}", Price3)#
        </td>
        <td class="js-number">
            #: number #
        </td>
    </tr>
</script>
 
<script type="text/x-kendo-tmpl" id="myPDFTemplate">
    #
    var theDate = new Date();
    #
    <div class="page-template">
        <div class="header">
            My Report
        </div>
        <div class="footer">
            <span>#=theDate.toLocaleDateString("en-US")#</span>
            <div style="float: right">Page #:pageNum# of #:totalPages#</div>
        </div>
    </div>
</script>
 
<script>
const getData = new ApiCall("GET", `/path/to/api/${record}`, null, createTable);
 getData.call();
// Note ApiCall is a custom function not included in this example
 
function createTable(data) {
    var myColumns = [
        { field: "category", title: "Category", width: "200px" },
        { field: "itemNumber", title: "Item Number", width: "130px" },
        { field: "itemName", title: "Description", width: "200px" },
        { field: "price1", title: "Price 1", width: "130px" },
        { field: "price2", title: "Price 2", width: "130px" },
        { field: "price3", title: "Price 3", width: "130px" },
        { field: "number", title: "Number", width: "130px" }
    ];
 
    $("#impactTable").kendoGrid({
        dataSource: {
            data: data
        },
        excel: {
            fileName: "grid.xlsx",
            //proxyURL: "https://demos.telerik.com/kendo-ui/service/export",
            filterable: true
        },
        pdf: {
            fileName: "grid.pdf",
            paperSize: "Letter",
            landscape: true,
            scale: 0.7,
            margin: { top: "0.5in", left: "0.25in", right: "0.25in", bottom: "0.5in" },
            title: "My Grid",
            multiPage: true,
            repeatHeaders: true,
            avoidLinks: true,
            template: $("#PDFTemplate").html()
        },
        rowTemplate: kendo.template($("#myRowTemplate").html()),
        columns: myColumns,
        sortable: true,
        toolbar: [
            { template: standardGridToolbar("myToolbar") }
        ],
        search: {
            fields: ["category", "itemNumber", "itemName", "number"]
        },
        dataBound: function (e) {
            $('#myTableSpinner').removeClass('show');
        }
    });
    $('#myTable .dropdown-trigger').dropdown();
}
</script>
0
Alex Hajigeorgieva
Telerik team
answered on 16 Dec 2019, 09:49 AM

Hi, Lee,

Thank you very much for the provided code snippets, it looks like in essence, the grid works with local data and we can easily mock the data on the client.

There are a couple of things that I noticed:

  1. The grid bound properties are lower cased while the row template features Pascal case properties, does that mean that the returned model is not the one same as the model in the grid?
  2. The row template seems unnecessary, you can format the price columns by specifying the schema model field type 
          var myColumns = [
            { field: "category", title: "Category", width: "200px" },
            { field: "itemNumber", title: "Item Number", width: "130px" },
            { field: "itemName", title: "Description", width: "200px" },
            { field: "price1", title: "Price 1", width: "130px", format: "{0:c}" },
            { field: "price2", title: "Price 2", width: "130px", format: "{0:c}" },
            { field: "price3", title: "Price 3", width: "130px", format: "{0:c}" },
            { field: "number", title: "Number", width: "130px"}
          ];
    
            dataSource: {
              data: data,
              schema:{
                model:{
                  fields:{
                    price1: { type: "number" },
                    price2: { type: "number" },
                    price3: { type: "number" }
                  }
                }
              }
            }

So with this in mind - the export of an identical grid as the one you shared takes 8 seconds for 759 records and 7 columns and is 40 pages:

https://dojo.telerik.com/@bubblemaster/IzIDaYUN/2

Perhaps in your case, the row template or some styles are bumping up the size of the export - we can take a look at those as well as reducing the pages will make a big difference.

The alternative to that is to force a proxy usage that helps somewhat and finally, you can look into the server side PDF processing but as demonstrated - this should not be required for the amount of data and configuration that you have. If you see something that we have missed in the latest Dojo test case, please let us know.

Kind Regards,
Alex Hajigeorgieva
Progress Telerik

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Tags
Grid
Asked by
Lee
Top achievements
Rank 1
Iron
Iron
Iron
Answers by
Alex Hajigeorgieva
Telerik team
Lee
Top achievements
Rank 1
Iron
Iron
Iron
Share this question
or