Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Grid - Export to PDF #14345

Closed
KuznetsovVN opened this issue Aug 24, 2020 · 29 comments
Closed

Data Grid - Export to PDF #14345

KuznetsovVN opened this issue Aug 24, 2020 · 29 comments

Comments

@KuznetsovVN
Copy link
Contributor

KuznetsovVN commented Aug 24, 2020

⚠️ The information contained within this page details our current vision of a feature/product design and is subject to change. You should not use this information in your production environment. We consider any feedback you share with us, but you should not expect it to be reflected in the final implementation.


The Problem

The DevExtreme DataGrid does not allow users to export grid data to PDF documents.

The Proposed Solution

We plan to integrate the third-party jsPDF library and the jsPDF-AutoTable plugin into the DataGrid API and enhance our exportDataGrid method to support export as a PDF.

The proposed solution allows for the following customizations:

  • Cell content
  • Addition of page headers and footers
  • Inclusion of custom content alongside the grid
  • Export of multiple grids into a one page PDF
  • Export of multiple grids into a multiple-page PDF

The export of the DataGrid with a custom color scheme is not currently supported.

The resulting PDFs will retain DataGrid settings such as grouping, bands, summaries, etc.

Implementation Details

The exportDataGrid method accepts the following object:

exportDataGrid({
    component: DataGrid, // A DataGrid instance
    pdfDoc: Object, // A jsPDF instance
    customizeCell: Function, // Customizes a grid cell
    autoTableOptions: Object // Configures the AutoTable plugin
});

Use Cases

Export DataGrid as is

const dataGridOptions = {
    onExporting: e => {
        const pdfDoc = new jsPDF('p', 'pt', 'a4');
        const options = {
            pdfDoc: pdfDoc,
            component: e.component
        };

        exportDataGrid(options).then(() => {
            pdfDoc.save('filePDF.pdf');
        });

        e.cancel = true;
    } 
};

GitHub Logo

Customize cell content

const dataGridOptions = {
    onExporting: e => {
        const pdfDoc = new jsPDF('p', 'pt', 'a4');
        const options = {
            pdfDoc: pdfDoc,
            component: e.component,
            customizeCell: (pdfCell, gridCell) => {
                if(gridCell.column.dataField === 'Picture') {
                    if(gridCell.rowType === 'data') {
                        pdfCell.content = '';
                        pdfCell.customDrawCell = (data) => {
                            pdfDoc.addImage(imageData, 'PNG', data.cell.x, data.cell.y, 50, 70);
                        };
                    }
                }
            }
        };

        exportDataGrid(options).then(() => {
	        pdfDoc.save('filePDF.pdf');
        });

        e.cancel = true;
    } 
};

GitHub Logo

Add header and footer

const buttonOptions = {
    text: 'Export Grids',
    onClick: () => {
        const pdfDoc = new jsPDF('p', 'pt', 'a4');
        const options = {
            pdfDoc: pdfDoc,
            component: dataGrid
        };

        exportDataGrid(options).then(() => {
            pdfDoc.setFontSize(12);
            const pageCount = pdfDoc.internal.getNumberOfPages();
            for (let i = 1; i <= pageCount; i++) {
                pdfDoc.setPage(i);
                const pageSize = pdfDoc.internal.pageSize;
                const pageWidth = pageSize.width ? pageSize.width : pageSize.getWidth();
                const pageHeight = pageSize.height ? pageSize.height : pageSize.getHeight();
                const header = 'Report 2014';
                const footer = `Page ${i} of ${pageCount}`;

                // Header
                pdfDoc.text(header, 40, 15, { baseline: 'top' });

                // Footer
                pdfDoc.text(footer, pageWidth / 2 - (pdfDoc.getTextWidth(footer) / 2), pageHeight - 15, { baseline: 'bottom' });
            }
        }).then(() => {
            pdfDoc.save('filePDF.pdf');
        });
    }
};

Watch the video

Include custom content alongside the grid

const buttonOptions = {
    text: 'Export Grids',
    onClick: () => {
        const pdfDoc = new jsPDF('p', 'pt', 'a4');
        const options = {
            pdfDoc: pdfDoc,
            component: dataGrid,
            autoTableOptions: {
                margin: { top: 15, left: 15 },
                tableWidth: 310
            }
        };

        exportDataGrid(options).then(() => {
            pdfDoc.addImage(imageBase64, 340, 15);

            const pageSize = pdfDoc.internal.pageSize;
            const pageWidth = pageSize.width ? pageSize.width : pageSize.getWidth();

            pdfDoc.setFontSize(10);
            pdfDoc.text(custom_text, 340, 96, {
                maxWidth: pageWidth - 360,
                align: 'justify',
                lineHeightFactor: 1.6,
                baseline: 'top'
            });
        }).then(() => {
            pdfDoc.save('filePDF.pdf');
        });
    }
};

GitHub Logo

Try It

Live Sandboxes

  1. Export DataGrid as is
  2. Customize cell content
  3. Add page header and footer
  4. Include custom content alongside the grid
  5. Export multiple grids into one page of a PDF document
  6. Export multiple grids into different pages of a PDF document

Installation

Import exportDataGrid, jsPDF, and jsPDF-AutoTable.

We Need Your Feedback

Take a Quick Poll

Do you find export to PDF useful for your projects?

Get Notified of Updates

Subscribe to this thread or to our Facebook and Twitter accounts for updates on this topic.

@ysageev
Copy link

ysageev commented Sep 2, 2020

I like this feature and the direction you guys are going with it. However, what I minimally need right now is to be able to specify where a dxDataGrid page breaks to account for footers.

Our use case: We (meaning I) are taking a huge amount of CRUD data and assembling a very large report with many sections, many grids, and other content including text, images, headers, footers.

The problem: Browsers have very limited support for the CSS Paged Media specification . In particular, they don't support margin locations. If I am printing a large report, I can't put a footer on with regular CSS:

.reportFooter { position: fixed; bottom: 0; }

This doesn't work because the datagrids and other content run "behind" the footer to the margin before breaking. If browsers supported the specification, I could simply specify that the footer goes in the "bottom-center" margin and everything would be great, since the the content rendered in the main section would never overrun the margin.

In the future I am going to try to get Paged.js working in my React app. Paged.js is a FOSS polyfill that attempts to implement the CSS Paged media spec. It is on npm. I guess what I really want is for DevExpress to apply its substantial technical chops to the project and then add special magic to make it more usable. I'm sorry if this substantially expands the scope of this initiative, perhaps prohibitively so. dxDataGrids actually print very nicely apart from the page break issue.

Anyway, this is where I am in the whole PDF/printing odyssey, which on the whole has been surprisingly disappointing. I can't believe browsers have such limited support for proper print layout, given that every non-tech business is addicted to printing.

@code123xy
Copy link

First of all, thanks for this great addition, we will use it a lot.
Is there a way how can I bind autotable options (like coloring headers)? For example, this is not working:
autoTableoptions: { theme: 'striped', useDxTheme :false, styles: { fillColor: [255, 0, 0] }, }

@KuznetsovVN
Copy link
Contributor Author

Hello, @code123xy

Is there a way how can I bind autotable options (like coloring headers)?

You need to use the headStyles option:
autoTableOptions: { headStyles: { fillColor: [255, 0, 0] } }

Here is a sample: https://codepen.io/kuznetsovvn/pen/bGpMQbe
Please, try it and share your results with me.

@bobbyconnolly
Copy link

If the customer wanted to print a paper copy of the datagrid, does the browser do a good job of printing the grid? Or do you recommend piping to PDF first and then printing? Thanks!

@KuznetsovVN
Copy link
Contributor Author

Hello, @bobbyconnolly

We do not officially support the browser printing functionality, and the resulting document may vary in different browsers. So, if it's possible, it's better to export the grid before printing it. This will allow you to have the same behavior and appearance across different browsers.

@PetarBgd
Copy link

Angular demo https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/ExportToPDF/Angular/Light/ does not work. Nothing happen on button click.

@KuznetsovVN
Copy link
Contributor Author

Hello, @PetarBgd

Thank you for notifying us of this issue.
Demos are a very important part of our products, and we want to investigate each case when something works in an unexpected way.
Yesterday, we found similar errors in demos on our site, made some changes to fix them, and today demos work correctly.
Anyway, thank you again, and feel free to write to us if the https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/ExportToPDF/Angular/Light/ demo still doesn't work or if you encounter other issues with our demos: create a new ticket at https://supportcenter.devexpress.com/ and describe what works incorrectly.

@apanchati
Copy link

Hello,

Will there be option to put columns on the second page? I have about 30 columns on my grid and would like to have 10 columns per page.

Thanks,
Ahmed

@KuznetsovVN
Copy link
Contributor Author

Hello, @apanchati

We currently have no plans to support this functionality.
Thank you for your suggestion, we will take your feedback into account during further development.
However, I cannot give you any promises or estimates here.
If we start working on this feature, it will appear in our Roadmap or on the GitHub page.

Should you have any questions, feel free to contact us.
We’ll be happy to help you.

@jsdmitry
Copy link
Contributor

jsdmitry commented Nov 5, 2020

Thank you to everyone who gave us early feedback. The Export to PDF (CTP) feature is now available in v20.2 release. We would appreciate it if you take a quick poll and leave your feedback in comments.

@H-M90
Copy link

H-M90 commented Nov 5, 2020

Hello,
I have a problem displaying Arabic characters in generating PDF.
I added an Arabic font but Arabic words not displayed and Arabic characters in the table appeared as a strange symbol.
I need to know what is my problem.
image

 $('#exportButton').dxButton({
        icon: 'exportpdf',
        text: 'Export to PDF',
        onClick: function() {
        const pdfDoc = new jsPDF('p', 'pt', 'a4');
        const options = {
            jsPDFDocument: pdfDoc,
            component: dataGrid
        };
        DevExpress.pdfExporter.exportDataGrid(options).then(() => {
            pdfDoc.setFontSize(12);
            pdfDoc.setLanguage("ar");
            pdfDoc.setFont("janna_regular");
            const pageCount = pdfDoc.internal.getNumberOfPages();
            for (let i = 1; i <= pageCount; i++) {
                pdfDoc.setPage(i);
                const pageSize = pdfDoc.internal.pageSize;
                const pageWidth = pageSize.width ? pageSize.width : pageSize.getWidth();
                const pageHeight = pageSize.height ? pageSize.height : pageSize.getHeight();
                const header = 'Report 2014';
                const footer = `Page ${i} of ${pageCount}`;

                // Header
                pdfDoc.text(header, 40, 15, { baseline: 'top' });

                // Footer
                pdfDoc.text(footer, pageWidth / 2 - (pdfDoc.getTextWidth(footer) / 2), pageHeight - 15, { baseline: 'bottom' });
            }
        }).then(() => {
            pdfDoc.save('filePDF.pdf');
        });
    }

@KuznetsovVN
Copy link
Contributor Author

Hello, @H-M90

The issue occurs because fonts are configured incorrectly in your application. For more information, please refer to this article: Use of Unicode Characters / UTF-8.

I have created a simple sample to demonstrate export with Arabic characters: https://codepen.io/kuznetsovvn/pen/abZaOJY.
Please try it and share your results with me.

@H-M90
Copy link

H-M90 commented Nov 7, 2020

Hello,
Thank you for your response.
I tried this method and now the Arabic characters are showing fine. But I faced a problem when I tried the RTL. All characters are reversed
image

image

 const pdfDoc = new jsPDF('p', 'pt', 'a4');
      pdfDoc.addFileToVFS("janna_regular.ttf", myFont);
      pdfDoc.addFont("janna_regular.ttf", "janna", "normal");
      pdfDoc.setFont("janna");
      pdfDoc.setR2L(true);

      const options = {
        jsPDFDocument: pdfDoc,
        component: dataGrid,
        autoTableOptions: {
          styles: {
            font: 'janna'
          }
        }
      };
      DevExpress.pdfExporter.exportDataGrid(options).then(() => {
        const pageCount = pdfDoc.internal.getNumberOfPages();
            for (let i = 1; i <= pageCount; i++) {
                pdfDoc.setPage(i);
                const pageSize = pdfDoc.internal.pageSize;
                const pageWidth = pageSize.width ? pageSize.width : pageSize.getWidth();
                const pageHeight = pageSize.height ? pageSize.height : pageSize.getHeight();
                const header = 'Report 2014';
                const footer = `Page ${i} of ${pageCount}`;

                // Header
                pdfDoc.text(header, 40, 15, { baseline: 'top' });
                
                // Footer
                pdfDoc.text(footer, pageWidth / 2 - (pdfDoc.getTextWidth(footer) / 2), pageHeight - 15, { baseline: 'bottom' });
            }
        }).then(() => {
            pdfDoc.save('filePDF.pdf');
        });

@KuznetsovVN
Copy link
Contributor Author

KuznetsovVN commented Nov 9, 2020

Hello, @H-M90

Please make sure that you are using the rtlEnabled option of our DataGrid and set the setR2L option of the resulting pdf document to false. In this case, the grid should be exported correctly. For example, please refer to the sample CodePen project from my previous comment.

If this does not help or if you have further questions related to your specific scenario, feel free to contact us in our Support Center - devexpress.com/ask. Our support team will be happy to assist you.

@antoni-sganzerla-reitech

Hello, I have a Data Grid with Master-Detail but when generating the pdf, the Master-Detail is not rendered, am I doing something wrong?

Here is my example project:
https://codesandbox.io/s/8o3u4?file=/src/app/app.component.ts

@KuznetsovVN
Copy link
Contributor Author

KuznetsovVN commented Jan 11, 2021

Hello, @antoni-sganzerla-reitech

At present, PDF Export is in CTP and we do not support the Master-Detail scenario. We will take your scenario into account during further development though I cannot give any promises or estimates here at the moment. If we start working on this feature, it will appear in our Roadmap or on the GitHub page.

@scss-sschultz
Copy link

Hello. Will it be possible to export dxCharts (etc) and dxDataGrids into one PDF? Like a sales dashboard with charts and a grid.

Thanks!

@KuznetsovVN
Copy link
Contributor Author

KuznetsovVN commented Feb 8, 2021

Hello, @scss-sschultz

You can include any custom content alongside the grid using the library's jspdf functionality. Please check the Include custom content alongside the grid example.
Also, I have created a simple sample to demonstrate how you can export dxChart and dxDataGrid into one PDF document.
https://codepen.io/kuznetsovvn/pen/zYorawq

@l3of0x
Copy link

l3of0x commented Mar 12, 2021

Would you consider an export option as "raw html" so that the table could be embedded into any application that supports HTML, for example Word?

@KuznetsovVN
Copy link
Contributor Author

KuznetsovVN commented Mar 15, 2021

Hello, @l3of0x

Thanks for sharing your idea with us, we will keep this feature in mind, but now it is not in our plans.
We are currently working on pdf export and once completed, we’ll take a look at raw html export.

@Javorov1103
Copy link

Helloe @KuznetsovVN How we are going to put some text under the grid. I mean to do that only once. For example Invoice with
total net price, total vat sum and total sum?

@KuznetsovVN
Copy link
Contributor Author

Hello @Javorov1103 ,

You can use the text method to write text in any position on the page.
To find the bottom position of the table, use the "finalY" option of the AutoTable plugin as shown in these examples:
https://github.com/simonbengtsson/jsPDF-AutoTable/blob/master/examples/examples.js

@singhinderpal
Copy link

singhinderpal commented May 11, 2021

Hi @KuznetsovVN,

We have a huge grid with more than 17 columns (count can go up even more) which We had to export to pdf.
To handle the scenario I used the following code which breaks the table into multiple pages horizontally.

autoTableOptions: { horizontalPageBreak: true, }

The problem with this approach is that the data is huge and it gets hard to understand what page/row belongs to which data.

It would be much better if the id column (which is currently fixed on UI) can be added on each subsequent page as well.
I am currently using v20.2.3 on angular.

@alekzvargas
Copy link

Hi @KuznetsovVN,

Thanks for posting this, I was having issues trying to do this.

I implemented this on a datagrid with barcode image columns (created with JSBarcode) and worked like a charm.

I'm using devextreme on MVC

Thanks again

@novsstation
Copy link
Contributor

We plan to replace jsPDF-AutoTable with our own module in the future.
Please subscribe to this page to receive notifications about this change.

@ignatiusvanniekerk
Copy link

Hi there,
is there a way of hiding rows that is collapsed when exporting the pdf. currently, it export all rows even though they are collapsed

@KuznetsovVN
Copy link
Contributor Author

Hello, sorry for the delay.
You can use jsPDF-AutoTable to manually generate the required output (see Usage). If you have no direct access to the grid's data, you can request it - get the grid's dataSource, use the store method to get the instance of the store underlying DataSource, then use the store's load method to load data.

@EugeniyKiyashko
Copy link
Contributor

Hello,

In v22.1, we introduced a new PDF exporting API.

The new implementation of exporting a DataGrid to a PDF document doesn't use the AutoTable plugin anymore. After using this plugin within the CTP period, our customers encountered a large number of issues that we could not affect and we could not suggest solutions in a reasonable time. We decided to stop using this plugin and implement our own mechanism for this purpose.

We kept the previous implementation's exportDataGrid function and you can find it in the pdf_exporter module:

import pdfExporter from 'devextreme/pdf_exporter'; 

(pdfExporter as any).exportDataGridWithAutoTable({
  ... 
});

It will be available for a few releases and help you make the migration to the new implementation smoother.

Currently, all options must be configured directly through the exportDataGridOptions properties that have a full set of settings that you may need to migrate.

We have collected the most common usage scenarios in our Widgets Gallery demos:

This information may help you understand how the new API can be used.

If you have additional questions, please submit a ticket to our Support Center. We'll review them and find the most suitable solution.

@Marileni
Copy link

Marileni commented Oct 5, 2022

Hello,

I have a problem displaying Greek characters in generating PDF.
Is there any way in order to fix this?

1

2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests