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
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

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();
}
});

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
>
004.
<
base
href
=
"https://demos.telerik.com/kendo-ui/grid/remote-data-binding"
>
005.
<
style
>html { font-size: 14px; font-family: Arial, Helvetica, sans-serif; }</
style
>
006.
<
title
></
title
>
007.
<
link
rel
=
"stylesheet"
href
=
"https://kendo.cdn.telerik.com/2019.3.1023/styles/kendo.bootstrap-v4.min.css"
/>
008.
009.
<
script
src
=
"https://kendo.cdn.telerik.com/2019.3.1023/js/jquery.min.js"
></
script
>
010.
<
script
src
=
"https://kendo.cdn.telerik.com/2019.3.1023/js/kendo.all.min.js"
></
script
>
011.
012.
<!-- Load Pako Deflate library to enable PDF compression -->
013.
<
script
src
=
"https://kendo.cdn.telerik.com/2019.3.1023/js/pako_deflate.min.js"
></
script
>
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: {
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
>
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

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

<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>
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:
- 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?
- 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