bpampuch / pdfmake

Client/server side PDF printing in pure JavaScript
http://pdfmake.org
Other
11.48k stars 2.03k forks source link

Snaking Columns #604

Open andrew-rutherford opened 8 years ago

andrew-rutherford commented 8 years ago

There appears to be no way to get a single block of text (and other elements) to overflow from one column into the next column (snaking columns). Currently, any overflow is pushed to the same column on the next page.

Would this functionality be difficult to add? I am surprised it is not already a feature as it is a core feature of PDFKit (however only text is supported).

I am not very familiar with the PDFMake codebase (have just had a few hours looking through it) and would be willing to have a go at adding this feature. Before I attempt this, if anyone familiar with the codebase could make any comments regarding the architectural feasibility of this enhancement or the reasoning behind columns having been implemented as discrete containers, it would be greatly appreciated.

I would also consider contributing (financially) to have this feature added if that idea appeals to anyone!

Thanks in advance.

matt-darby commented 7 years ago

Also requesting this feature.

Did you manage to implement it andrew?

robertknecht commented 7 years ago

Yes, this is definitely a needed feature for my work.

RobertPrediger commented 7 years ago

+1

michaelb87 commented 7 years ago

+1

Michfou commented 6 years ago

+1

leontalbot commented 6 years ago

+1

kngchn commented 6 years ago

+1

aqelkp commented 6 years ago

+1 Much needed feature

Quixomatic commented 5 years ago

@liborm85 any updates on this feature? Looks like this open issue hasn't been touched in quite some time.

ninasaveljeva commented 5 years ago

I was able to made floating columns only with raw PDFKit. Why pdfmake does not support this feature while PDFKit does and pdfmake based on pdfkit?

Quixomatic commented 5 years ago

I was able to made floating columns only with raw PDFKit. Why pdfmake does not support this feature while PDFKit does and pdfmake based on pdfkit?

@ninasaveljeva, You're right and even in the pdfkit demo it shows snaking column: http://pdfkit.org/demo/browser.html

@liborm85 Any insights/updates related to this?

memoryonrepeat commented 4 years ago

@corneliouzbett Your example doesn't work. I tried expanding the first column to see if it overflows to the next column, and the result is that it goes to next page instead. Try it on your own:

var dd = {
    content: [
    {
     style: 'tableExample',
     layout: 'noBorders',
    table: {
       widths: ['*', '*'],
        body: [
            ['Parent 1', 'Parent 2'],
             [
                [
                    {
                        table: {
                            widths: ['*', '*'],
                            body: [
                                ['Col1', 'Col2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],
                                ['1', '2'],

                            ]
                        },
                    }
                ],
                [
                    {
                        table: {
                             widths: ['*', '*'],
                         body: [
                                ['Col3', 'Col4'],
                                ['3', '4'],
                                ['3', '4']
                            ]
                        },
                    }
                ]
            ]
        ]
    }
}]  
}
riacnhk commented 4 years ago

Would love to see this feature as well. There seems to be interest but it's been a while - any updates on this feature?

Luis945 commented 4 years ago

need it in my work, +1

bsparacino commented 4 years ago

I could also use this feature

fmmendes commented 4 years ago

I need this also

MussaCharles commented 4 years ago

@liborm85 Any updates about this feature?

virgileMaon commented 4 years ago

@liborm85 Any updates about ths feature?

nikolnikov commented 3 years ago

I'm also incredibly interested in this and I'm willing to financially help achieve this functionality.

AliMariam commented 3 years ago

@liborm85 Any updates about ths feature?

dwissing commented 3 years ago

This would be very useful. I need this and currently don't have a work-around.

matsgm commented 3 years ago

+1

r-breu commented 3 years ago

+1

gfriesen98 commented 2 years ago

+1

jonnycraze commented 2 years ago

+1

asajadi84 commented 2 years ago

+1

mjr21a commented 2 years ago

+1

erneu commented 2 years ago

:+1: I'd need this feature too!

MelanieMorin commented 2 years ago

👍 I'd need this feature too!

me too !

Benni77 commented 1 year ago

me to! 6 years from opening this issue...

JakubELMI commented 1 year ago

me too!

theguybyte commented 1 year ago

+1.

iDeliverDev commented 1 year ago

:+1: I'd also like this feature, I'm in a position to contribute financially. If there's a PDFMake maintainer that wants to contact me (it@ideliver.net.au) I can arrange it.

gus01488 commented 1 year ago

I'd need this feature too!

sepsol commented 1 year ago

I need this feature as well. Below is the screenshot of the current behavior vs the desired behavior:

Problem-Showcase-2

Elaborating on @memoryonrepeat's comment, you could also use the sample document definition object below in the pdfmake's playground if you want to see the lack of "snaking columns" behavior for yourself:

Sample Document Definition
```js var dd = { content: [ { columns: [ { table: { widths: ['*', '*'], headerRows: 1, body: [ ['Col1', 'Col2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ['1', '2'], ], }, }, { table: { widths: ['*', '*'], body: [ ['Col3', 'Col4'], ['3', '4'], ['3', '4'], ], }, }, ], }, ], }; ```

Guidance from AI

I am attempting to work on a solution for this issue. I am going to share comments I receive from AI here in case it inspires anyone or anybody wants to elaborate.

Describe the project structure and logic.
> `pdfmake` is a PDF document generation library for server-side and client-side in pure JavaScript. It uses a layout engine to create a document definition and then renders it into a PDF document. > > The layout engine logic resides in the `LayoutBuilder` module, which is required in the `printer.js` file. The `layoutBuilder.js` module is responsible for taking the document definition and calculating the position and size of each element in the document. It does this by executing the layout engine on the document definition object and creating an array of pages containing positioned blocks, lines, and inlines. > > The `LayoutBuilder` constructor takes in the page size, page margins, image measure, and SVG measure as parameters. It initializes a `TraversalTracker` object to keep track of the current position in the document, and an empty object to store table layouts. > > The `LayoutBuilder` instance has a `registerTableLayouts` method that allows registering custom table layouts. > > The `layoutDocument` method takes in the document definition, font provider, style dictionary, default style, background, header, footer, images, watermark, and a page break function as parameters. It first preprocesses the document definition using the `DocPreprocessor` module to flatten the document structure and resolve references to styles and fonts. > > It then creates a `DocumentContext` object that contains information about the current page, position, and style context stack. It initializes the page context with the page size and margins. > > The `layoutDocument` method then iterates over the document definition and processes each node in the document using the `processNode` method. The `processNode` method calculates the size and position of the node and adds it to the current page. If the node overflows the current page, a new page is created and the node is added to the new page. > > The `layoutDocument` method also handles page breaks by calling the `pageBreakBeforeFct` function for each node and adding a page break if the function returns true. > > Finally, the `layoutDocument` method returns an array of pages containing positioned blocks, lines, and inlines. > > Overall, the `layoutBuilder.js` module is a crucial part of the `pdfmake` library that enables the creation of complex PDF documents with precise layout and positioning of elements. >
How does pdfmake handle content overflow across pages?
> When a text or table element overflows across pages, the `LayoutBuilder` module uses a `PageElementWriter` object to write the element to the current page and handle the overflow to the next page. > > For text elements, the `PageElementWriter` object uses the `TextTools` module to split the text into lines that fit within the available space on the current page. It then writes the lines to the current page and returns the remaining text that overflows to the next page. > > For table elements, the `PageElementWriter` object uses the `TableProcessor` module to split the table into rows that fit within the available space on the current page. It then writes the rows to the current page and returns the remaining rows that overflow to the next page. > > The `PageElementWriter` object keeps track of the current position on the page and the remaining available space. When an element overflows to the next page, it creates a new page and continues writing the element to the new page. > > The `LayoutBuilder` module also uses a `PageContext` object to keep track of the current page size, margins, and available space. When an element overflows to the next page, the `PageContext` object is updated with the new page size and margins, and the available space is reset. > > To determine where to split the text or table, the `TextTools` and `TableProcessor` modules use the `measureText` and `measureTable` methods, respectively, to calculate the size of the text or table. They then compare the size to the available space on the current page to determine where to split the element. > > Overall, the `LayoutBuilder` module uses a combination of `PageElementWriter`, `PageContext`, `TextTools`, and `TableProcessor` objects to handle content overflow across pages in a precise and efficient manner. >
How can one implement a solution to this outstanding issue?
> Adding the desired feature requires understanding how pdfmake internally manages the column flow. Given the complexity and the desired behavior, it's not an easy task, but I'll provide a roadmap for achieving the effect: > > 1. **Update the Document Definition Schema**: > To allow users to specify how they want columns to overflow, you need to update the document definition. You could add a flag, e.g., `interColumnOverflow: true`, in the column definition which dictates that overflow content should first go to the next column before going to the same column on the next page. > > 2. **Modify `PageElementWriter.js`**: > The primary file managing how content is written onto a page seems to be `PageElementWriter.js`. This is where you'll make the bulk of your changes. > > a. **Add Column Logic in `moveToNextPage`**: Modify the `moveToNextPage` function. Before moving to a new page, check if the overflow should instead go to the next column. If you're at the last column, only then move to the next page. > > b. **Enhance Overflow Logic**: Modify the overflow handling functions (`addLine`, `addImage`, `addSVG`, `addQr`, and `addFragment`). When content doesn't fit in the current column, and `interColumnOverflow` is true, you should attempt to put it in the next column. > > 3. **Update `layoutBuilder.js`**: > This file contains the logic for interpreting and laying out the document definition onto pages. Any logic changes in how content should flow in terms of column-to-column before page-to-page would also need some adjustments here. > > a. **Column Iteration**: As columns are processed, keep track of the current column number. This will help when you're deciding whether to move content to the next column or the next page. > > b. **Overflow Strategy**: When content overflows and the `interColumnOverflow` flag is true, try to add content to the next column. Only move to the next page when you're at the last column. > > 4. **Consider Complex Use Cases**: > - What happens if the next column is too short to fit the overflow content? > - How do you handle situations where there are different amounts of content in each column, making the columns unbalanced? > - Consider scenarios with nested columns or tables. How does the new logic play with those nested structures? > > 5. **Testing**: > After implementing changes, extensive testing is crucial. Ensure that: > > a. Existing behavior remains unaffected when the `interColumnOverflow` flag is not used. > > b. New behavior works as expected when the `interColumnOverflow` flag is used. > > c. Test corner cases and complex document definitions to ensure stability. > > 6. **Documentation**: > Update the pdfmake documentation to explain the new `interColumnOverflow` flag, how it works, and any limitations. >
bergheim commented 9 months ago

How did it go - any progress here @sepsol ?

sepsol commented 8 months ago

@bergheim, sorry for the late response. Yes, I am still working on this, my fork is available here. So far, I was able to sort of create the desired behavior as shown below. But it is still very buggy and rough around the edges. Any contribution is welcome!


Text Content

BEFORE AFTER
![image](https://github.com/bpampuch/pdfmake/assets/47871887/ce84a015-4aaa-4061-abdc-ff4daa03602e) ![image](https://github.com/bpampuch/pdfmake/assets/47871887/a3498f4d-3b41-4fc4-a49c-4980476a0fbc)


Table Content

BEFORE AFTER
![image](https://github.com/bpampuch/pdfmake/assets/47871887/385af60e-9bd4-42f8-a0a3-9ef9e4cb1c0f) ![image](https://github.com/bpampuch/pdfmake/assets/47871887/8c6d4455-c684-4d2d-9e9c-aab70ec705fd)