简体   繁体   中英

Creating multiple pdfs with pdf2htmljs

I wrote the following code to create several pdf files with html2pdf.js. Later I want to save them to an archive and download said archive. I am using jsZip to create and fill the archive and fileSaver to save it:

window.zip = new JSZip();
let masterDiv = document.createElement('div');
masterDiv.id = 'masterDiv';
document.body.appendChild(masterDiv);

// Stundenplan für jeden Kandidaten erstellen
for (let i = 0; i < window.allCandidates.length; i++) {
  let candidate = window.allCandidates[i].split(',');
  window.lastName = candidate[0];
  window.firstName = candidate[1];
  window.filter = 'candidate';
  setScheduleView('masterDiv');

  let worker = html2pdf();

  let opt = {
    margin: 1,
    image: {
      type: 'jpeg',
      quality: 1
    },
    html2canvas: {
      scale: 2
    },
    jsPDF: {
      unit: 'mm',
      format: 'A3',
      orientation: 'portrait'
    }
  };

  // PDF erstellen
  worker.set(opt).from(masterDiv).outputPdf().then((pdf) => {
    window.zip.file(window.lastName + '.' + window.firstName + '.pdf', pdf, {
      binary: true
    });
  });

  setTimeout(() => {
    clearDiv(masterDiv);
    worker = null;
  }, 50);
}

setTimeout(() => {
  window.zip.generateAsync({
    type: "blob"
  }).then(function(content) {
    saveAs(content, "allPDFs.zip");
  });
}, 2000);

My problem is now: instead of one pdf per loop I get one single pdf that contains the complete content. What do I have to change to make html2pdf.js understand that it should create a new pdf file for each loop?

It seems you are not properly waiting for async operations to finish, in particular the one that clears the div is probably executed before the pdf is generated and the same can happen with the zip generation, you got several options here depending on how you want to process the pdfs:

Secuentially with async/await:

// ...
async function generatePDFs(){
    for (let cand of window.allCandidates) {
        let candidate = cand.split(',');
        // ...
        // wait for every worker to finish before the next iterarion
        await worker.set(opt).from(masterDiv).outputPdf().then((pdf) => {
            window.zip.file(window.lastName + '.' + window.firstName + '.pdf', pdf, {
                binary: true
            });
            clearDiv(masterDiv);
        });
    }

    window.zip.generateAsync({
        type: "blob"
    }).then(function (content) {
        saveAs(content, "allPDFs.zip");
    });
}

Secuentially with reduce: in case you cannot use async/await

// create an array of functions so that each one of them will
// generate the pdf for a candidate  and then clear the div.
let pdfGenerators = allCandidates.map((cand) => {
    return () => {
        let candidate = cand.split(',');
        // ...
        return worker.set(opt).from(masterDiv).outputPdf().then((pdf) => {
            window.zip.file(window.lastName + '.' + window.firstName + '.pdf', pdf, {
                binary: true
            });
            clearDiv(masterDiv);
        });
    }
});
// call reduce on your pdf generators array starting from a resolved promise, so
// everytime a  promise is resolved you enqueue the next one, after that generate the 
// zip
pdfGenerators.reduce((pre, curr) => pre.then(curr), Promise.resolve()).then(() => {
    window.zip.generateAsync({
        type: "blob"
    }).then(function (content) {
        saveAs(content, "allPDFs.zip");
    })
});

Parallel with Promise.all : for this approach (and in general) stop using global variables and instead create a function that declares everything that needs to be read in order to generate a PDF:

// ...
function generatePDF(candidate, i){
    let div = document.createElement('div');
    div.id = `candidate-${i}`;
    document.body.append(div);
    setScheduleView(div.id);
    let [lastName, firstName] = candidate.split(',');
    // ...
    return worker.set(opt).from(div).outputPdf().then((pdf) => {
        window.zip.file(lastName + '.' + firstName + '.pdf', pdf, {
            binary: true
        });
        clearDiv(div);
    });
}
// wait for all pdfs to be generated (in parallel)
Promise.all(allCandidates.map(generatePDF)).then(() => {
    window.zip.generateAsync({
        type: "blob"
    }).then(function (content) {
        saveAs(content, "allPDFs.zip");
    });
});

In order to handle asynchronous programming in JS properly read about Promises and Async functions .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM