PdfPig icon indicating copy to clipboard operation
PdfPig copied to clipboard

Add functionality to calculate existing global transformation matrix or add content absolutely

Open svengeance opened this issue 9 months ago • 2 comments

Yo! Thanks for the work you've done here, and @BobLd for taking over to give Elliot a well-deserved break.

Given the requirement of adding a QR code to the bottom-right corner of the page, I've ended up in a bit of a goose chase. Stamping code:

PdfPageBuilder.AddedImage? pdfImage = null;

foreach (var originalPage in pdfDocument.GetPages())
{
	var copiedPage = db.AddPage(pdfDocument, originalPage.Number);
	var pdfImageLocation = new PdfRectangle(
		new PdfPoint(originalPage.Width - image.Width, 0),
		new PdfPoint(originalPage.Width, image.Height)
	);

	if (pdfImage is null)
		pdfImage = copiedPage.AddJpeg(imageStream, pdfImageLocation);
	else
		copiedPage.AddImage(pdfImage, pdfImageLocation); // Reuses the same image for all pages
}

return db.Build();

This worked for several PDFs, but failed on Chromium-produced PDFs. After spending an incredibly loathsome amount of time learning about how PDFs work, I understand the issue to be that these Chromium PDFs begin their content stream with a strange transformation matrix that inverts the Y-axis and applies a 25% scaling

0.23999999 0 0 -0.23999999 0 792 cm

This caused my QR placement to be completely out of sync for these documents.


My solution was to wrap the page copying into a push/pop of the graphics state:

var newPage = db.AddPage(originalPage.Width, originalPage.Height);

newPage.CurrentStream.Operations.Add(Push.Value);
newPage.CopyFrom(originalPage);
newPage.CurrentStream.Operations.Add(Pop.Value);

// Add QR as expected

This eliminates the global matrix applied at the top of the chromium PDF. And, thank the Gods, it works.


My suggestion for this library is to add some functionality to either (or both!):

1: Provide a method that will iterate over the content stream and calculate the "current" transformation matrix 2: Enhance image-writing methods to allow them to be absolutely positioned. This could be implemented by pushing, inverting the current transformation matrix, writing the image, then popping to restore.

If I've gone about this the hard way and there's an easier way to do what I've done, I'm also all ears.

For your convenience I've attached 3 PDFs - the chromium PDF with a naive stamp before the push/pop fix, the same PDF with my push/pop fix, and a PDF without a global transformation matrix (produced by PdfPig).

stamped-chromium-before.pdf stamped-chromium.pdf stamped-pdfpig.pdf

Thank you for your time.

svengeance avatar Mar 12 '25 16:03 svengeance

After looking at some more of PdfPig's code I did find that there is a current transformation matrix that's available in the stream processor that runs to build the resultant PDF. So I guess my proposal may be more narrowed to a desire to be able to absolutely position content. I'm not sure if that's in-scope for this library or if it's too high level though.

svengeance avatar Mar 12 '25 23:03 svengeance

Should be addressed by #1094

EliotJones avatar Jul 20 '25 01:07 EliotJones