Search code examples
javascriptangulartypescriptpdfmake

How can I split a page in half horizontally with same content on both sides?


I Have some text, a small logo and a table. It would look like this:

[Text]    [Logo]
[     table    ]

I want to add a duplicate copy of it, below it while adding a horizontal rule. I can use a 0 height table as a horizontal rule, but the problem is if the table has less than the maximum (11 or so) rows it isn't centred vertically. Centered vertically as in the horizontal rule should divide the page in half. The content and duplicate of the content would be on the divided sections. something like this:

 ___________________
|                   |
| [Office Copy]     |
| [Text]     [Logo] |
| [     table     ] |
|                   |
| [horizontal rule] |
|                   |
| [Other Copy]      |
| [Text]     [Logo] |
| [     table     ] |
|___________________|

Code used to get current functionality:

    const fontSize = 9;
    const styles = {
      companyName: {
        fontSize: fontSize + 2,
        bold: true,
      },
      subheader: {
        fontSize: fontSize + 5,
        bold: true,
        margin: [0, 0, 0, 5]
      },
      table: {
        fontSize: fontSize,
        margin: [0, 10, 0, 10],
        alignment: 'center',
      },
      tableHeader: {
        fontSize: fontSize,
        bold: true,
        alignment: 'center',
      },
      textRight: {
        fontSize: fontSize,
        alignment: 'right',
      },
      textLeft: {
        fontSize: fontSize,
        alignment: 'left',
      },
      default: {
        fontSize: fontSize,
        alignment: 'center',
      },
    };

    const today = new Date();
    const companyDetails = [
      {
        columns: [
          {
            width: '50%',
            text: [... Some text],
            style: 'textLeft',
          },
          {
            width: '*',
            image: this.imageLogo,
            fit: [30, 30],
            alignment: 'right'
          }
        ]
      },
    ];

    const dcDetails = [
      { text: `SubHeading`, style: 'subheader', alignment: 'left' },
      {
        margin: [0, 0, 0, 10],
        columns: [
          {
            width: '50%',
            text: [... SomeDetails],
            style: 'textLeft'
          },
          {
            width: '50%',
            text: [... SomeOtherDetails],
            style: 'textRight',
          },
        ]
      },
    ];

    const tableWidths = ['5%', '10%', '*', '6%', '15%'];
    const tableHeader = [
      { text: 'NO', style: 'tableHeader', fillColor: '#38751d', color: 'white' },
      { text: 'HSN/SAC', style: 'tableHeader', fillColor: '#38751d', color: 'white' },
      { text: 'DESCRIPTON', style: 'tableHeader', fillColor: '#38751d', color: 'white' },
      { text: 'QTY', style: 'tableHeader', fillColor: '#38751d', color: 'white' },
      { text: 'VALUE', style: 'tableHeader', fillColor: '#38751d', color: 'white' },
    ];
    const tableFooter = [
      [
        { text: '', fillColor: 'lightgreen', fillOpacity: 0.4 },
        { text: '', fillColor: 'lightgreen', fillOpacity: 0.4 },
        { text: 'Total', style: 'tableHeader', fillColor: 'lightgreen', fillOpacity: 0.4, alignment: 'right' },
        { text: '', fillColor: 'lightgreen', fillOpacity: 0.4 },
        {
          text: `Rs. ${this.indianCurrencyPipe.transform(this.pdfSellingCostTotal)}`,
          style: 'tableHeader', alignment: 'right', fillColor: 'lightgreen', fillOpacity: 0.4
        },
      ],
      [
        { text: '', fillColor: 'lightgreen', fillOpacity: 0.4 },
        { text: '', fillColor: 'lightgreen', fillOpacity: 0.4 },
        {
          text: `INR ${Utils.getFormattedNumberToWords(this.pdfSellingCostTotal)}`,
          style: 'tableHeader', alignment: 'right', fillColor: 'lightgreen', fillOpacity: 0.4, colSpan: 3,
        },
      ],
    ];

    const testTableContent = [
      { text: '1' },
      { text: '12312' },
      { text: 'Product Name, long description long description long description', style: 'textLeft' },
      { text: '99999' },
      { text: `${this.indianCurrencyPipe.transform(165000)}`, style: 'textRight' },
    ];
    const testTableContentString = JSON.stringify(testTableContent);

    const table = {
      style: 'table',
      table: {
        // headers are automatically repeated if the table spans over multiple pages
        // you can declare how many rows should be treated as headers
        headerRows: 1,
        widths: tableWidths,
        body: [
          tableHeader,
          testTableContent,                  //1
          JSON.parse(testTableContentString),//2
          JSON.parse(testTableContentString),//3
          JSON.parse(testTableContentString),//4
          JSON.parse(testTableContentString),//5
          JSON.parse(testTableContentString),//6
          JSON.parse(testTableContentString),//7
          JSON.parse(testTableContentString),//8
          JSON.parse(testTableContentString),//9
          JSON.parse(testTableContentString),//10
          ...this.addedStocksPdfContent,
          ...tableFooter,
        ]
      }
    };

    const page = [
      ...companyDetails,
      ...dcDetails,
      table,
    ];

    const horizontalRule = {
      table: { widths: ['*'], body: [[" "], [" "]] },
      layout: {
        hLineWidth: (i, node) => ((i === 0 || i === node.table.body.length) ? 0 : 2),
        vLineWidth: (i, node) => (0),
      }
    };

    const docDefinition = {
      info: {
        title: `Delivery Challan`,
        // author: 'author name',
        // subject: 'subject of document',
        // keywords: 'keywords for document',
      },
      pageSize: 'A4',
      pageOrientation: 'portrait',
      pageMargins: [30, 30, 30, 30],
      styles: styles,
      content: [
        [{ text: 'Office Copy', bold: true, fontSize: fontSize + 1 }],
        page,
        horizontalRule,
        [{ text: 'Transporter Copy', bold: true, fontSize: fontSize + 1 }],
        JSON.parse(JSON.stringify(page))], // ! PERF: structuredClone is faster
    };

    const dcPdf = pdfMake.createPdf(docDefinition);
    dcPdf.getBlob(data => {
      const blob = new Blob([data], { type: 'application/pdf' });
      const pdfFile = new File([blob], 'test.pdf');
      saveAs(pdfFile); // ! REMOVE IMPORT
      // sendMail(pdfFile);
    });


Solution

  • Pdfmake still hasn't developed a vertical-align feature yet.

    However if your page content's height (the logo, text and table elems) is not going to change, I would suggest using the margin attribute like so :

     ___________________
    |                   |
    | [Office Copy]     |
    | [Text]     [Logo] |
    | [     table     ] | // has margin bottom to lower line
    |                   |
    | [your line here]  | // has margin bottom to lower other copy
    |                   |
    | [Other Copy]      |
    | [Text]     [Logo] |
    | [     table     ] |
    |___________________|
    

    Draw a line using the canvas elem, and add bottom margin like :

    // in table elem for example    
    {table: {body: [YOUR CONTENT], margin: [0, 0, 0, 20]}}
    

    Hope it was clear, cheers