Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snaking Columns #604

Open
andrew-rutherford opened this issue Jun 10, 2016 · 37 comments
Open

Snaking Columns #604

andrew-rutherford opened this issue Jun 10, 2016 · 37 comments

Comments

@andrew-rutherford
Copy link

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
Copy link

Also requesting this feature.

Did you manage to implement it andrew?

@robertknecht
Copy link

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

@RobertPrediger
Copy link

+1

@michaelb87
Copy link

+1

3 similar comments
@Michfou
Copy link

Michfou commented Aug 29, 2017

+1

@leontalbot
Copy link

+1

@kngchn
Copy link

kngchn commented Dec 6, 2017

+1

@aqelkp
Copy link

aqelkp commented Dec 27, 2017

+1 Much needed feature

@Quixomatic
Copy link

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

@ninasaveljeva
Copy link

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
Copy link

Quixomatic commented May 30, 2019

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
Copy link

@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
Copy link

riacnhk commented Jan 22, 2020

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
Copy link

Luis945 commented Feb 18, 2020

need it in my work, +1

@bsparacino
Copy link

I could also use this feature

@fmmendes
Copy link

fmmendes commented Apr 2, 2020

I need this also

@MussaCharles
Copy link

@liborm85 Any updates about this feature?

@virgileMaon
Copy link

@liborm85 Any updates about ths feature?

@nikolnikov
Copy link

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

@AliMariam
Copy link

@liborm85 Any updates about ths feature?

@dwissing
Copy link

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

@matsgm
Copy link

matsgm commented Jun 11, 2021

+1

5 similar comments
@r-breu
Copy link

r-breu commented Jun 17, 2021

+1

@gfriesen98
Copy link

+1

@jonnycraze
Copy link

+1

@asajadi84
Copy link

+1

@mjr21a
Copy link

mjr21a commented Apr 28, 2022

+1

@erneu
Copy link

erneu commented May 9, 2022

👍 I'd need this feature too!

@MelanieMorin
Copy link

👍 I'd need this feature too!

me too !

@Benni77
Copy link

Benni77 commented Jul 11, 2022

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

@JakubELMI
Copy link

me too!

@theguybyte
Copy link

+1.

@iDeliverDev
Copy link

👍 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
Copy link

I'd need this feature too!

@sepsol
Copy link

sepsol commented Jul 7, 2023

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
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
Copy link

How did it go - any progress here @sepsol ?

@sepsol
Copy link

sepsol commented Oct 30, 2023

@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

image


Table Content

BEFORE AFTER

image

image

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

No branches or pull requests