Jspdf-autotable: Multi-section table - wrap table columns in the same page

Created on 4 Apr 2020  路  8Comments  路  Source: simonbengtsson/jsPDF-AutoTable

Is it possible to create multiple columns on a page and have a table wrap its data among those columns?

This was brought up in #287 and closed but it does not appear it was ever addressed. I have tried looking through the commits and source code and have not found any parameters on how to accomplish this.

multi-col-table

enhancement question informative

Most helpful comment

That's a very interesting idea.

Currently, the plugin doesn't support this out of the box .. but you can actually implement this yourself using hooks.

The general idea is very simple, add a hook for didDrawPage and then for each table page that should be drawn in the same page : doc.setPage(previous_page) and shift the table position to the right :

function createRows(count) {
  const rows = [];
  for (let i = 1; i <= count; i++) {
    rows.push([i, `Name ${i}`, `Email ${i}`])
  }
  return rows;
}

function generatePDF(save = false) {
  const doc = new jsPDF('p', 'mm', 'a4');

  // overall margin
  const margin = {
    left: 15,
    right: 15,
    top: 20,
    bottom: 20,
  };

  const rowsCount = 80;

  // number of table sections in the page
  const sections = 2;

  // space between each section
  const spacing = 5;

  // calculate each section width
  const printWidht = doc.internal.pageSize.width- (margin.left + margin.right);
  const sectionWidth = (printWidht - ((sections - 1) * spacing)) / sections;

  // add initial empty page that we will delete later,
  // we need it so when we activate it the next addPage()
  // for the second table page will be on the actial first page
  doc.addPage();

  doc.autoTable({
    head: [['ID', 'Name', 'Email']],
    body: createRows(rowsCount),
    tableWidth: sectionWidth,
    margin,
    rowPageBreak: 'avoid', // avoid breaking rows into multiple sections
    didDrawPage({table, pageNumber}) {
      const docPage = doc.internal.getNumberOfPages();
      const nextShouldWrap = pageNumber % sections;

      if (nextShouldWrap) {
        // move to previous page, so when autoTable calles
        // addPage() it will still be the same current page
        doc.setPage(docPage - 1);

        // change left margin which will controll x position
        table.settings.margin.left += sectionWidth + spacing;
      } else {
        // reset left margin for the first section in every page
        table.settings.margin.left = margin.left;
      }
    }
  });

  // delete unused empty page
  doc.deletePage(1)

  doc.save('multi-section table.pdf');
}

Here is the codepen
Also an html file you can download multi-section-table.zip

(I may add it as an example in the examples section of the repo)
@simonbengtsson what do you think?

All 8 comments

That's a very interesting idea.

Currently, the plugin doesn't support this out of the box .. but you can actually implement this yourself using hooks.

The general idea is very simple, add a hook for didDrawPage and then for each table page that should be drawn in the same page : doc.setPage(previous_page) and shift the table position to the right :

function createRows(count) {
  const rows = [];
  for (let i = 1; i <= count; i++) {
    rows.push([i, `Name ${i}`, `Email ${i}`])
  }
  return rows;
}

function generatePDF(save = false) {
  const doc = new jsPDF('p', 'mm', 'a4');

  // overall margin
  const margin = {
    left: 15,
    right: 15,
    top: 20,
    bottom: 20,
  };

  const rowsCount = 80;

  // number of table sections in the page
  const sections = 2;

  // space between each section
  const spacing = 5;

  // calculate each section width
  const printWidht = doc.internal.pageSize.width- (margin.left + margin.right);
  const sectionWidth = (printWidht - ((sections - 1) * spacing)) / sections;

  // add initial empty page that we will delete later,
  // we need it so when we activate it the next addPage()
  // for the second table page will be on the actial first page
  doc.addPage();

  doc.autoTable({
    head: [['ID', 'Name', 'Email']],
    body: createRows(rowsCount),
    tableWidth: sectionWidth,
    margin,
    rowPageBreak: 'avoid', // avoid breaking rows into multiple sections
    didDrawPage({table, pageNumber}) {
      const docPage = doc.internal.getNumberOfPages();
      const nextShouldWrap = pageNumber % sections;

      if (nextShouldWrap) {
        // move to previous page, so when autoTable calles
        // addPage() it will still be the same current page
        doc.setPage(docPage - 1);

        // change left margin which will controll x position
        table.settings.margin.left += sectionWidth + spacing;
      } else {
        // reset left margin for the first section in every page
        table.settings.margin.left = margin.left;
      }
    }
  });

  // delete unused empty page
  doc.deletePage(1)

  doc.save('multi-section table.pdf');
}

Here is the codepen
Also an html file you can download multi-section-table.zip

(I may add it as an example in the examples section of the repo)
@simonbengtsson what do you think?

Wow thanks for this, the pdf you generated is exactly the type of output I am looking to achieve. Now I know what I will be working on in the morning.

@mmghv
One option to add to the table is:
startY: margin.top

While working through my specific use case, I am adding hundreds of separate tables to the reports, each table is put onto a new page. After calling doc.addPage(), if the previous page's table was writing to the second section (right side section), then the new table added to the next page would have an incorrect startY value. The table was being offset down the page, it seemed like it was retaining the Y value from the previous table.

So it would be best practice to specify the startY value for each added table.
If you are only working with 1 table then it is not necessary to do this.

This is a pretty simple solution and I really appreciate how quickly you came up with a solution!

function createRows(count) {
  const rows = [];
  for (let i = 1; i <= count; i++) {
    rows.push([i, `Name ${i}`, `Email ${i}`])
  }
  return rows;
}

function generatePDF(save = false) {
  const doc = new jsPDF('p', 'mm', 'a4');

  // overall margin
  const margin = {
    left: 15,
    right: 15,
    top: 20,
    bottom: 20,
  };

  const rowsCount = 80;

  // number of table sections in the page
  const sections = 2;

  // space between each section
  const spacing = 5;

  // calculate each section width
  const printWidht = doc.internal.pageSize.width- (margin.left + margin.right);
  const sectionWidth = (printWidht - ((sections - 1) * spacing)) / sections;

  // add initial empty page that we will delete later,
  // we need it so when we activate it the next addPage()
  // for the second table page will be on the actial first page
  doc.addPage();

  doc.autoTable({
    startY: margin.top
    head: [['ID', 'Name', 'Email']],
    body: createRows(rowsCount),
    tableWidth: sectionWidth,
    margin,
    rowPageBreak: 'avoid', // avoid breaking rows into multiple sections
    didDrawPage({table, pageNumber}) {
      const docPage = doc.internal.getNumberOfPages();
      const nextShouldWrap = pageNumber % sections;

      if (nextShouldWrap) {
        // move to previous page, so when autoTable calles
        // addPage() it will still be the same current page
        doc.setPage(docPage - 1);

        // change left margin which will controll x position
        table.settings.margin.left += sectionWidth + spacing;
      } else {
        // reset left margin for the first section in every page
        table.settings.margin.left = margin.left;
      }
    }
  });

  // delete unused empty page
  doc.deletePage(1)

  doc.save('multi-section table.pdf');
}

Yes, of course you will need to customize it to your specific needs.
you may want to tweak it to print page header/footer, or to join multiple tables .. etc.

For example, here's a version that draws multiple tables in a joint continues section flow, it might be a little crazy but it shows how much control you have when using hooks :

multi table

codepen

Yep, I have a good understanding now of what you did and how to manipulate it, I spent all of yesterday creating different reports.
This is a pretty simple library used compared to the C# and PHP libraries I have previously used which just creates a mess of code.

Adding an example sounds great @mmghv!

Hello!
Thank you for the information on sections, but it just happened to me that my table ended right at the end of the sheet and it generates a new sheet for me.
Using your example the same thing happens if I generate the tables with a total of 32 rows, eg: if I have 3 tables with 32 rows this generates 3 sheets in the PDF.

What could I do to prevent it from turning the page in this case and generating it in the next section?

codepen

@PedidosClear Fixed that now in the codepen :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

simonbengtsson picture simonbengtsson  路  4Comments

bsparacino picture bsparacino  路  3Comments

simonbengtsson picture simonbengtsson  路  5Comments

webdream-fr picture webdream-fr  路  6Comments

mmghv picture mmghv  路  4Comments