Skip to content

Instantly share code, notes, and snippets.

@poissoj
Created April 7, 2017 19:30
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save poissoj/d79a520ca2a3377c15e902ec68790aff to your computer and use it in GitHub Desktop.
Save poissoj/d79a520ca2a3377c15e902ec68790aff to your computer and use it in GitHub Desktop.
Split image for pdfMake
const PAGE_HEIGHT = 700;
const PAGE_WIDTH = 500;
const content = [];
function getPngDimensions (base64) {
const header = atob(base64.slice(22, 70)).slice(16, 24);
const uint8 = Uint8Array.from(header, c => c.charCodeAt(0));
const dataView = new DataView(uint8.buffer);
return {
width: dataView.getInt32(0),
height: dataView.getInt32(4)
};
}
const splitImage = (img, content, callback) => () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const printHeight = img.height * PAGE_WIDTH / img.width;
canvas.width = PAGE_WIDTH;
for (let pages = 0; printHeight > pages * PAGE_HEIGHT; pages++) {
/* Don't use full height for the last image */
canvas.height = Math.min(PAGE_HEIGHT, printHeight - pages * PAGE_HEIGHT);
ctx.drawImage(img, 0, -pages * PAGE_HEIGHT, canvas.width, printHeight);
content.push({ image: canvas.toDataURL(), margin: [0, 5], width: PAGE_WIDTH });
}
callback();
};
function next () {
/* add other content here, can call addImage() again for example */
pdfMake.createPdf({ content }).download();
}
function addImage () {
/* Load big image */
const image = 'data:image/png;base64,iVBORw0Kgo…';
const { width, height } = getPngDimensions(image);
const printHeight = height * PAGE_WIDTH / width;
if (printHeight > PAGE_HEIGHT) {
const img = new Image();
img.onload = splitImage(img, content, next);
img.src = image;
return;
}
content.push({ image, margin: [0, 5], width: PAGE_WIDTH });
next();
}
addImage();
@HarshSaudagar
Copy link

function getPngDimensions (base64) {
    const header = atob(base64.slice(22, 70)).slice(16, 24);
    const uint8 = Uint8Array.from(header, c => c.charCodeAt(0));
    const dataView = new DataView(uint8.buffer);

    return {
        width: dataView.getInt32(0),
        height: dataView.getInt32(4)
    };
}

New to Javascript and web deveIopment. I didn't understand what this function does exactly? Could please explain ?
Especially const header = atob(base64.slice(22, 70)).slice(16, 24);.

Thanks in advance

@xreign
Copy link

xreign commented Apr 24, 2018

Any angularjs version of this?

@kriscoleman
Copy link

AngularJS should just work with this... it just runs JS and that's all this is.

@keego
Copy link

keego commented Jan 13, 2019

Thanks for the code and insight @Balkoth. The split image definitely splits the image well and puts the different parts on different pages, but I noticed the image quality noticeably degraded. When using images small enough where splitImage was not needed, quality seemed great. My project doesn't support ES6 so I had to convert this code to ES5, and I also decided to promisify things a bit, namely addImage but those were all the changes I made.

@keego
Copy link

keego commented Jan 15, 2019

Managed to work around the quality degradation issue by using html2canvas to snapshot many smaller nodes as opposed to the whole page at once. Then instead of having to split one big image, a bunch of the smaller images can be concatenated and naturally overflow to the next page. This gives the consumer better control of how a site can be split up into multiple pdf pages. I posted a forked gist for reference (note its ES5).

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