pdfmake icon indicating copy to clipboard operation
pdfmake copied to clipboard

pagination reset after group

Open singletondejan opened this issue 7 years ago • 8 comments

As PDF document containing "n" groups we would like to reset the page number after each group in the footer. As result each group will get printed out documents with pagination related to own group (e.g. "page 1 of 3" instead of e.g. "page 29 of 123").

Is this feature supported? If not, is it planned?

singletondejan avatar Jan 30 '18 17:01 singletondejan

Marked as feature request.

liborm85 avatar Jan 31 '18 08:01 liborm85

Stuck on this issue, can't wait to see this feature.

sinnawat avatar May 21 '19 06:05 sinnawat

Any updates so far??

ghost avatar May 30 '19 06:05 ghost

Or at least a workaround :(

ghost avatar Jun 01 '19 22:06 ghost

Table of Content skip the cover page number ( cover page doesn't have number but TOC counts it ) #1607 any update?

benjamin555 avatar Jun 23 '19 02:06 benjamin555

Grouping for pagination reset - work around.

I have created a workaround that functions, but could be cleaner if it were incorporated to be handled within pdfMake.

The content of my document definition may contain multiple different documents, which I wanted to be able to group so that their page footer would reset the page number and page count depending on the grouping instead of the entire document.

In order to get this to work, I had to use pdfMake.createPdf(docDefinition) twice. The first time it runs, all of the actual page numbers are assigned to the pages inside of the document definition. Inside of each "Group" of pages, I created a text object with a GroupStart at the beginning of the grouped pages and a text object with GroupEnd at the end of the group. When going thru the content of the document definition, I was then able to find the actual pages the Groups started and ended on and store those in an object array.

In order for pdfMake to be able to use the Groups object array, I then run pdfMake.createPdf(docDefinitionCopy) again with a cloned copy of the document definition. I found that you can't use the original document definition since pdfMake adds some entries which could push the pages to be different the second pass. So by creating a unique clone of the document definition before running it the first time, and then using the clone on the 2nd pass it kept the page numbers the same each time.

In the document definition, you still use the header and footer to define the header for each page, but inside of those is where the check is done to see if the currentPage is actually inside of a group and if then use the GroupPage and GroupPageCount. Instead of definining the header and footer within the document definition, I pass a javascript function.

In the function for the header or footer, if the page is not in a Group, then I have it use a default header or footer definition, otherwise I check to see if there is a GroupHeader/GroupFooter defined for the Group within the contents and will use that if it exist, or use a default

Group Header/Footer definition. Make sure the text object has a blank space " " and not and empty string "" for both the GroupStart and GroupEnd or it will not be given a pageNumber by pdfMake.

`var Content = [ {text: " ", GroupStart:true, GroupName:"Affidavit", GroupFooter:function(GroupPageNumber, GroupPageCount) { return {text: "Affidavit Page " + GroupPageNumber + " of " + GroupPageCount, fontSize:8 }; }, GroupHeader:function(GroupPageNumber, GroupPageCount) { return {text: "Affidavit", fontSize:8 }; } }, {text: "All of the pages of content for the group "},

                {text: " ", GroupEnd:true, GroupName:"Affidavit" },
              ];`

Here's where I add the report contents of multiple pages by concatenating them together and then passing them to my CreatePDF function, so that it will add them to my document definition and then start the group header/footer parsing.

`function CreatePDF(ReportsContent) { var docDefinitionCopy;

var docDefinition = {
    footer: function(currentPage, pageCount) { return pdf_Footer(currentPage, pageCount); },
    header: function(currentPage, pageCount) { return pdf_Header(currentPage, pageCount); },
    styles: pdfStyles,
    defaultStyle: pdfDefaultStyle,
    pageMargins: pdf_DefaultPageMargins,
    content: ReportsContent,
    images: {
        GPD_TriBadge: base64_Images.GPD_TriBadge,
        MunicipalCourtSeal: base64_Images.MunicipalCourtSeal,
    }
}

// docDefinition should be defined in the file using this layout
var pdfDocGenerator;

docDefinitionCopy = clone(docDefinition);

pdfDocGenerator = pdfMake.createPdf(docDefinition);

createGroupFootersHeaders(docDefinition);

pdfDocGenerator = pdfMake.createPdf(docDefinitionCopy);

pdfDocGenerator.getDataUrl((dataUrl) => {
    const targetElement = document.querySelector('#iframeContainer');
    const iframe = document.createElement('iframe');
    iframe.style.width = "100%";
    iframe.style.height = "100%";
    iframe.src = dataUrl;
    targetElement.appendChild(iframe);
});

}`

Below is the actual processing for determining the groups and their page numbers, determining if an actual page is within a group, and the two functions for creating the page header and footers.

` function pdf_Header(currentPage, pageCount) { var header; var UseGroupHeader = false;

//If this page is in a Group, and it has a header defined, use it instead
var GroupNumber = pdf_IsPageInGroup(currentPage);

if ( GroupNumber != -1 ) {
    var GroupPage = currentPage - pdf_Groups[GroupNumber].GroupPageStart + 1;
    var GroupPageCount = pdf_Groups[GroupNumber].GroupPageEnd - pdf_Groups[GroupNumber].GroupPageStart + 1;

    if ( pdf_Groups[GroupNumber].GroupHeader != undefined ) {
        header = pdf_Groups[GroupNumber].GroupHeader(GroupPage, GroupPageCount);
        UseGroupHeader = true;
    } else {
        header = {text: pdf_Groups[GroupNumber].GroupName };
        UseGroupHeader = true;
    }
}

//If not using a Group Header, then use this one.
if (! UseGroupHeader) {
    header = {text: "" };
}

return header;

}

function pdf_Footer(currentPage, pageCount) { var footer; var UseGroupFooter = false;

//If this page is in a Group, and it has a footer defined, use it instead
var GroupNumber = pdf_IsPageInGroup(currentPage);

if ( GroupNumber != -1 ) {
    var GroupPage = currentPage - pdf_Groups[GroupNumber].GroupPageStart + 1;
    var GroupPageCount = pdf_Groups[GroupNumber].GroupPageEnd - pdf_Groups[GroupNumber].GroupPageStart + 1;

    if ( pdf_Groups[GroupNumber].GroupFooter != undefined ) {
        footer = pdf_Groups[GroupNumber].GroupFooter(GroupPage, GroupPageCount);
        UseGroupFooter = true;
    } else {
        footer = {text: "Page " + GroupPage + " of " + GroupPageCount };
        UseGroupFooter = true;
    }
}

//If not using a Group Footer, then use this one.
if (! UseGroupFooter) {
    footer = {text: "Page " + currentPage.toString() + " of " + pageCount };
}

return footer;

}

//If the currentPage is in a group, return the group number from the array index. function pdf_IsPageInGroup(p) { for (x=0; x < pdf_Groups.length; x++) { if (p >= pdf_Groups[x].GroupPageStart && p <= pdf_Groups[x].GroupPageEnd) { return x; } } return -1; }

//Array to hold the Group Information var pdf_Groups = [];

function createGroupFootersHeaders(dd) { var InGroup = false; var Grouping = 0;

for (x=0; x <= dd.content.length; x++) {
    try {
        if (dd.content[x].GroupStart != undefined && dd.content[x].GroupStart == true) {
            //If we are already in another group that wasn't closed for some reason, close it before starting a new one
            if (InGroup) {
                if (pdf_Groups.length > 0) {
                    pdf_Groups[Grouping].GroupPageEnd = dd.content[x].positions[0].pageNumber - 1;  //End at previous page
                    Grouping++;
                }
            }

            pdf_Groups.push({ GroupName: dd.content[x].GroupName, GroupPageStart: dd.content[x].positions[0].pageNumber, GroupPageEnd: dd.content[x].positions[0].pageNumber, GroupHeader: dd.content[x].GroupHeader, GroupFooter: dd.content[x].GroupFooter });
            InGroup = true;
        } else if (dd.content[x].GroupEnd !== undefined && dd.content[x].GroupEnd == true) {
            pdf_Groups[Grouping].GroupPageEnd = dd.content[x].positions[0].pageNumber;
            Grouping++;
            InGroup = false;
        } else {
            if (!InGroup) {
                //Not in a defined group, so create one.
                pdf_Groups.push({ GroupName: dd.content[x].GroupName, GroupPageStart: dd.content[x].positions[0].pageNumber, GroupPageEnd: dd.content[x].positions[0].pageNumber, GroupHeader: dd.content[x].GroupHeader, GroupFooter: dd.content[x].GroupFooter });
                InGroup = true;
            }
        }
    }
    catch(ex) {
        //console.log("ERROR: createGroupFootersHeaders: " + ex.message);
    }
}

console.log(pdf_Groups);

} `

I hope this helps and perhaps something similar can be added to pdfMake itself. Instead of a seperate text object for GroupStart and GroupEnd, it would probably be easier to incorporate the contents of the group within a Group { } object itself.

Bernie

BernLaw avatar Jul 23 '19 21:07 BernLaw

Hi @BernLaw ,

Thanks for your contribution! I it is amazing how much effort you put in this. I'd really prefer to have this functionality integrated in pdfmake, so that as a user of of the library, I can consume this as a property. This is a must have and frankly, because this feature is missing, we stopped using pdfmake in new implementations.

Cheers, Dejan

singletondejan avatar Jul 24 '19 07:07 singletondejan

Hoping for this functionality soon...or I will also need to move away from using PDFMake.

MyQuestion123 avatar Mar 22 '20 00:03 MyQuestion123