Dynamic content height overlap issue

The PDF I am attempting to generate contains three sections per page: A header and footer on every page (Where the footer changes dynamically), and the main page content, which would ideally go between the header and footer.

I say “ideally” because while the header is correctly positioned above the content, the content overlaps the footer. To be specific, I create a page, add my header to it, and configure the page generation event to add the footer, and simply add all of my content to that page (Which has the unfortunate side effect of making it impossible to know the number of total pages at the time I add a footer!).

The content, for my testing, is a series of identical blue rectangular SVGs. Something like 30 of them, four of which fit on the page.

But I would prefer to only allow three to fit - and not by adding them manually, but by limiting the area they can flow into per page. The content that will ultimately be on these pages will have dynamic heights, so I won’t be able to craft these pages manually. What I need is a footer margin or something. Except no margins or rectangles in the API seem to do this.

Does anyone have any ideas? I can post some code, but it seems to me like I’m probably just missing some obscure property.

@Magus

Thank you for contacting support.

We would like to share with you that you can manipulate several page properties as explained in Get and Set Page Properties. These properties are also explained diagrammatically, in suggested documentation article. In case you face any problem, please share a narrowed down sample application along with the source and generated files, and expected output so that we may investigate further to help you out.

The following is the anonymized version of my code, though you may have to find a replacement SVG. What I can tell you for sure is that the documentation page you linked does not do what it says it does. For reference, I am using the latest NuGet package for Aspose.PDF, 18.4.1.

Things that do not do anything:

  • ProcessParagraphs(), when run, leaves the number of pages in the document at 1, directly contradicting its purpose as defined in your documentation.
  • The Rectangle-typed properties. Yes, I can change them, and some of them have a noticeable effect: they will move all content on the page. What I need is to control the area that, when overflowed, generates a new page. This is functionality that exists, and I’m willing to believe it is simply not exposed, but I definitely want to find out.
class Program
{
	static void Main(string[] args)
	{
		const string output = "C:\\doc\\hello2.pdf";
		const string companyName = "XYZ Company";

		var report = new MoreSpecificReport(output, companyName);
		report.CreatePdf();
	}
}

public class MoreSpecificReport : Report
{
	public MoreSpecificReport(string output, string companyName)
		: base(output, companyName, "Actual Real Report")
	{

	}

	protected override IEnumerable<BaseParagraph> InsertParagraphs()
		=> new BaseParagraph[]
		{
			new TextFragment("Hello"),
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			},
			new Image
			{
				File = "C:\\doc\\rect.svg",
				FileType = ImageFileType.Svg
			}
		};
}

public abstract class Report
{
	private string outputFile;
	private string companyName;
	private string reportName;
	private const string copyrightLine = @"This is a copyright line.";
	private readonly HeaderFooter header;

	public Report(string outputFile, string companyName, string reportName)
	{
		this.outputFile = outputFile;
		this.companyName = companyName;
		this.reportName = reportName;

		header = new HeaderFooter
		{
			Paragraphs =
			{
				new Table
				{
					ColumnWidths = "33%",
					Rows =
					{
						new Row
						{
							Cells =
							{
								CreateHeaderCell(HorizontalAlignment.Left, companyName),
								CreateHeaderCell(HorizontalAlignment.Center, reportName),
								CreateHeaderCell(HorizontalAlignment.Right, "Our Company's Name")
							}
						}
					}
				}
			}
		};
	}

	public void CreatePdf()
	{
		var document = new Document();
		var newPage = document.Pages.Add();
		newPage.Header = header;

		//var modRect = newPage.Rect;
		//newPage.TrimBox = new Rectangle(modRect.LLX, modRect.LLY + 300, modRect.URX, modRect.URY);
		newPage.OnBeforePageGenerate += OnPageGenerate;

		var paragraphs = InsertParagraphs();
		document.ProcessParagraphs(); // And yet, after this line runs, there is still only one page.

		foreach(var paragraph in InsertParagraphs())
		{
			newPage.Paragraphs.Add(paragraph);
		}

		document.Save(outputFile);
	}

	private void OnPageGenerate(Page page)
	{
		page.Footer = CreateFooter(page.Number);
	}

	protected abstract IEnumerable<BaseParagraph> InsertParagraphs();

	private HeaderFooter CreateFooter(int pageNumber, int maximumPageNumber = 0)
	{
		var footer = new HeaderFooter();
		var footerTable = new Table
		{
			ColumnWidths = "50%",
			HorizontalAlignment = HorizontalAlignment.Center
		};
		var locationRow = footerTable.Rows.Add();
		locationRow.Cells.Add(new Cell
		{
			Paragraphs =
			{
				new TextFragment(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss tt"))
				{
					HorizontalAlignment = HorizontalAlignment.Left
				}
			}
		});

		var pagingFragment = new TextFragment
		{
			Segments = {
				new TextSegment("Page "),
				new TextSegment(pageNumber.ToString())
				{
					TextState = new TextState
					{
						FontStyle = FontStyles.Bold
					}
				},
			},
			HorizontalAlignment = HorizontalAlignment.Right
		};

		if(maximumPageNumber > 0)
		{
			pagingFragment.Segments.Add(new TextSegment(" of "));
			pagingFragment.Segments.Add(new TextSegment(maximumPageNumber.ToString())
			{
				TextState = new TextState
				{
					FontStyle = FontStyles.Bold
				}
			});
		}

		locationRow.Cells.Add(new Cell
		{
			Paragraphs =
			{
				pagingFragment
			}
		});
		footer.Paragraphs.Add(footerTable);
		footer.Paragraphs.Add(new TextFragment
		{
			Segments =
			{
				new TextSegment(copyrightLine)
				{
					TextState = new TextState
					{
						FontSize = 7
					}
				}
			},
			Margin = new MarginInfo(0, 0, 0, 2)
		});

		return footer;
	}

	private Cell CreateHeaderCell(HorizontalAlignment alignment, string content)
	{
		var cell = new Cell
		{
			Alignment = alignment,
			Margin = new MarginInfo(0, 0, 0, 20)
		};

		if (!string.IsNullOrWhiteSpace(content))
		{
			var text = new TextFragment(content)
			{
				HorizontalAlignment = alignment
			};
			text.TextState.FontStyle = FontStyles.Bold;
			text.TextState.FontSize = 18;
			cell.Paragraphs.Add(text);
		}

		return cell;
	}
}

EDIT: Supplementary information: I have tried interacting with the various rectangles, but so far only the CropBox affects overflow in any way, but it changes the entire page size. I’m sure there has to be a way, though.

@Magus

We would like to request you to create separate topics for each problem you observe in your environment. Regarding this overflow approach, there has been an issue reported for problem with overflow of SVG images to next page if there is not sufficient space for it. Please try using PNG or JPG images instead of SVG images and then notice how these rectangles of a PDF page work with those images.

In case you do not see expected results, please create a single ZIP directory including the SVG files and a working narrowed down sample application reproducing this issue, instead of sharing the code snippet. Upload requested data to Google Drive, Dropbox etc. and share the download link with us, so that we may proceed to help you out.

I have commented on multiple things, but have only been asking for one thing. I understand that you agree that there is some kind of problem with SVGs, but can you please explain it?

I’m not asking a complicated question. Look, I’ll even draw a picture:

image.png (11.1 KB)

The left is what I’m getting. The blue boxes continue until the edge of the page, and then start on the next. I want to constrain the area they can fill. How can I do that? Do I have to lay them out manually?

@Magus

Thank you for elaborating it further.

We do understand your requirements, however, we would like to request you to check if this overflow occurs with SVG images only or if it occurs with JPG, PNG images as well. Ideally, setting page margins or other rectangular values for the page must not allow the images to overflow to the footer. We intend to narrow down and trace the problem you are facing; that is why we have requested you to check if this happens only with SVG or with all images. Kindly share your findings along with sample data and an application reproducing this issue, so that we may proceed to help you out.

So, just as a test, I did this with some tables and with some text fragments. Text fragments do indeed avoid overlapping the footer, but tables throw an exception on overflow, and don’t even create a new page. As full tables must be moved to the next page for the product I’m working on, and there are free products that have no problem with this, I have decided to forgo my efforts to make use of Aspose for this.

Thanks for your help!

@Magus

We would like to share with you that overflow of a table is a different scenario which can be manipulated by using several properties including IsBroken, IsKeptWithNext, IsInNewPage etc. which are exposed by Table Class. Kindly share a sample application reproducing the problem you are facing, so that we may take care of your concerns.