pdfmake icon indicating copy to clipboard operation
pdfmake copied to clipboard

100% table width, with auto column sizing

Open DataTables opened this issue 10 years ago • 18 comments

While looking to make my tables 100% width, I found #44:

set at least one column to star-sizing

So I tried setting all of the columns * but that results in all columns having the same width.

So I tried * in the first item and auto for the others:

widths: $.map( data.header, function (d, i) {
    return i===0 ? '*' : 'auto';
} ),

But that just added the extra width to the first column.

So I'm wondering if there is a way to have the auto column width, but with 100% table width.

Another option might be if there is a method to get the page width, I could compute the sizes to use based on my HTML table.

DataTables avatar Jun 26 '15 15:06 DataTables

Having just tried to use PDFMake myself, I find the inability to set the total width to 100% for the chosen page size and then setting setting the widths to say auto, *, * so that your first column is sized to the content and the rest of the page is evenly distributed between columns 2 and 3, to be a very limiting capability and makes the functional use of creating PDF based on a table nearly useless.

I know this question has been posted a long time ago, is there is any feedback from the developer on this? @bpampuch

GuardianMajor avatar Oct 27 '15 18:10 GuardianMajor

I'm trying to use 100% width with datatables, anyone know how?

BitPopCoin avatar Jan 25 '16 15:01 BitPopCoin

From what I can tell a page is 600 wide less any margins. So:

100% = 600 - (marginLeft + marginRight)

DougHayward avatar May 25 '16 09:05 DougHayward

bump!

fr0z3nfyr avatar Aug 08 '17 11:08 fr0z3nfyr

I found a good implementation for DataTables, I added a star auto sizing option for the table widths. It basically works by figuring out the full auto sized width of all the columns and than stretches that to 100% of the available width that is left. Star sizing kind of seemed weird to me because it gives all of the columns the same width when I think naturally they just want it to fit the page. Anyway its a starting point and it works great for me, let me know if it helps anybody and we can try to get it added to the repository

Changes to pdfmake.js (version v0.1.33):

line 23083 replace the buildColumnWidths function with this modified version:

	function buildColumnWidths(columns, availableWidth) {
		var autoColumns = [],
			autoMin = 0, autoMax = 0,
			starColumns = [],
			starAutoColumns = [],
			starMaxMin = 0,
			starMaxMax = 0,
			starAutoColWidth = 0,
			fixedColumns = [],
			initial_availableWidth = availableWidth;
		
		columns.forEach(function (column) {
			if (isAutoColumn(column)) {
				autoColumns.push(column);
				autoMin += column._minWidth;
				autoMax += column._maxWidth;
			} else if (isStarColumn(column)) {
				starColumns.push(column);
				starMaxMin = Math.max(starMaxMin, column._minWidth);
				starMaxMax = Math.max(starMaxMax, column._maxWidth);
			} else if (isStarAutoColumn(column)){ 
				starAutoColumns.push(column);
		 	} else {
				fixedColumns.push(column);
			}
			starAutoColWidth += column._minWidth;
		});

		starAutoColumns.forEach(function (column) {
			column._calcWidth = initial_availableWidth*(column._minWidth/starAutoColWidth);
		});

		fixedColumns.forEach(function (col) {
			// width specified as %
			if (typeof col.width === 'string' && /\d+%/.test(col.width)) {
				col.width = parseFloat(col.width) * initial_availableWidth / 100;
			}
			if (col.width < (col._minWidth) && col.elasticWidth) {
				col._calcWidth = col._minWidth;
			} else {
				col._calcWidth = col.width;
			}

			availableWidth -= col._calcWidth;
		});

		// http://www.freesoft.org/CIE/RFC/1942/18.htm
		// http://www.w3.org/TR/CSS2/tables.html#width-layout
		// http://dev.w3.org/csswg/css3-tables-algorithms/Overview.src.htm
		var minW = autoMin + starMaxMin * starColumns.length;
		var maxW = autoMax + starMaxMax * starColumns.length;
		if (minW >= availableWidth) {
			// case 1 - there's no way to fit all columns within available width
			// that's actually pretty bad situation with PDF as we have no horizontal scroll
			// no easy workaround (unless we decide, in the future, to split single words)
			// currently we simply use minWidths for all columns
			autoColumns.forEach(function (col) {
				col._calcWidth = col._minWidth;
			});

			starColumns.forEach(function (col) {
				col._calcWidth = starMaxMin; // starMaxMin already contains padding
			});
		} else {
			if (maxW < availableWidth) {
				// case 2 - we can fit rest of the table within available space
				autoColumns.forEach(function (col) {
					col._calcWidth = col._maxWidth;
					availableWidth -= col._calcWidth;
				});
			} else {
				// maxW is too large, but minW fits within available width
				var W = availableWidth - minW;
				var D = maxW - minW;

				autoColumns.forEach(function (col) {
					var d = col._maxWidth - col._minWidth;
					col._calcWidth = col._minWidth + d * W / D;
					availableWidth -= col._calcWidth;
				});
			}

			if (starColumns.length > 0) {
				var starSize = availableWidth / starColumns.length;

				starColumns.forEach(function (col) {
					col._calcWidth = starSize;
				});
			}
		}
	}

The only difference is that it utilizes the isStarAutoColumn function if it is being used but I figured this would be easier than copying and pasting on different lines inside the funciton.

line 23183 add:

function isStarAutoColumn(column){
	return column.width === null || column.width === undefined || column.width === '%';
}

And inside of your datatables options use this inside of your customize function for your pdf button:

doc.content[0].table.widths =
         Array(doc.content[0].table.body[0].length + 1).join('%').split('');

Jbegley1995 avatar Oct 26 '17 14:10 Jbegley1995

doc.content[1].table.widths = Array(doc.content[1].table.body[0].length + 1).join('%').split('');

But actually it wrongly distributing the width, column with less data taking more width than column with more data. Is there any solution?

azizrahaman avatar Dec 30 '17 23:12 azizrahaman

It works perfectly in mine, if you could provide an example maybe I could help out?

Jbegley1995 avatar Dec 31 '17 20:12 Jbegley1995

My god I have been trying to find this solution for the longest time.

You are a gentlemen and a scholar.

Thanks for sharing that function.

pstoneg avatar Mar 27 '18 16:03 pstoneg

@Jbegley1995 , excellent solution. It seems to have a bug though if you use mixed fixed widths and star-auto columns, like this: doc.content[1].table.widths=["50","%","%"];

pkExec avatar Apr 03 '18 08:04 pkExec

Isn't there any generic way to get a 100% width table. I find the above code is an incredible overhead for this small feature?

hitautodestruct avatar Jul 09 '19 06:07 hitautodestruct

any updates? how to set up the entire table to the 100% available width?

nickensoul avatar Mar 18 '20 10:03 nickensoul

up

dayrontbs12001 avatar Apr 14 '20 16:04 dayrontbs12001

I was/am in strict need of this feature as well, but I decided to try to add the support to pdfmake, inspired by this thread. If it's of interest to others I can potentially add a test etc as well and submit a pull request.

The change itself is fairly basic and as nonintrusive as I could come up with. I added support for a new width type called "auto*" which kind of behaves as a combination of "auto" and "*", so whenever there is more space available than strictly needed any "auto*" columns will scale up proportionally until all space is filled. So essentially you get a full width table, but you can combine it with "auto" so certain columns won't grow.

It fills my need so far, so I though I might ask what others think. Can be seen here: 8a7e430f27d1608f2d7f7c18735d4f12fe6b89af

kennylovrin avatar Jan 05 '21 17:01 kennylovrin

If there is header in your table then

table : { headerRows: 1, width:['auto','*','*'], body:[ [{text:'Count', style:'tableHeader'}, {text:'Alias', style:'tableHeader', margin:[80,0,80,0]}, {text:'Asset(s)', style:'tableHeader', margin:[80,0,80,0]}], ['1', 'Aasdjlalskjdflsjfldsjfjfslfljfjsdlajsdlad', 'Asstt'] ] } adding margin at header also helps sometimes.

krishankantray avatar Jan 16 '21 16:01 krishankantray

doc.content[0].table.widths = Array(doc.content[0].table.body[0].length + 1).join('*').split(''); make table width full fit to page

mjy12 avatar Dec 01 '21 17:12 mjy12

doc.content[1].table.widths = Array(doc.content[1].table.body[0].length + 1).join('*').split('');

RodSebDaS avatar Jan 18 '23 05:01 RodSebDaS

customize: function (doc) { doc.content[1].table.widths = Array(doc.content[1].table.body[0].length + 1).join('*').split(''); #for full fit to page doc.content[1].table.widths = [ 50, 'auto', '*' ]; #customize if you know sum of columns ex. 3 cols }

rachmd1987 avatar Apr 19 '23 06:04 rachmd1987

It works for me

table: {
    ...
    widths: cols.map((_, i) => {
      const colsTotal = cols.length
      if (i + 1 === colsTotal) {
        return "*"
      }
      return `${100 / colsTotal}%`
    }),
    ...
  },

henriquesml avatar Mar 03 '25 12:03 henriquesml