Skip to content

Make geometric transforms consistent with PDF specification#51

Merged
BobLd merged 3 commits intoBobLd:0.1.12-skia3from
PsykerUdot:consistent-transformations
Nov 7, 2025
Merged

Make geometric transforms consistent with PDF specification#51
BobLd merged 3 commits intoBobLd:0.1.12-skia3from
PsykerUdot:consistent-transformations

Conversation

@PsykerUdot
Copy link
Copy Markdown
Contributor

@PsykerUdot PsykerUdot commented Nov 3, 2025

This pull request refactors the Skia rendering pipeline to meet PDF specifications and make coordinate transformations fully affine, so not only point coordinates are transformed but the whole shape.

Changes involve simplifying transformation logic and standardizing coordinate usage.

These changes require also changes in UglyToad/PdfPig: UglyToad/PdfPig#1198

Rendering pipeline simplification and fixes:

  • Transformations are applied directly to the Skia's canvas.
  • Removed most usages of transformation matrices and height-based coordinate adjustments in rendering methods (e.g., SKRect, SKPoint, path drawing).
  • Dynamic stroke scaling factor is not required anymore, as line width is implicitly affected by current transformation of canvas.
  • Shading and tiling patterns rendering conforms Paragraph 8.7.2 (Note 1) of PDF specification regarding mapping pattern space to parent content stream.
  • Type1 (Function-based) Shading uses SKShader created from image.
  • Workaround to handle cases when no path-painting operators were encountered in ill-formed PDF content. This actually requires more fundamental fix.

Test coverage additions:

  • Added new test cases for PDF samples with rotated and transformed content.

Notes

Test case PDFBOX-1869-4 shows some differences to expected image. That is because Type1 (Function-based) Shading implementation had flaws in terms of coordinate transformations.

During development I targeted SkiaSharp 3.119.1, as version 2.88.* seems completely outdated.

Comment on lines +171 to +173
_canvas.Concat(textMatrix.ToSkMatrix());
_canvas.Concat(renderingMatrix.ToSkMatrix());
_canvas.Scale(1, -1, 0, 0);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API specs on DrawShapedText are pretty scarce. No ideas why text must be flipped along Y axis.

Comment on lines +183 to +204
if (_updateCurrentStreamOriginalTransform)
{
// Update the original transform for form XObject
_currentStreamOriginalTransforms.Push(CurrentTransformationMatrix.ToSkMatrix());

_updateCurrentStreamOriginalTransform = false;
}
}

// Note that recursive calls are possible here for nested form XObjects
protected override void ProcessFormXObject(StreamToken formStream, NameToken xObjectName)
{
// Indicate that we want to update the original transform for form XObject
_updateCurrentStreamOriginalTransform = true;

base.ProcessFormXObject(formStream, xObjectName);

// Restore previous original transform
_currentStreamOriginalTransforms.Pop();
}

private bool _updateCurrentStreamOriginalTransform;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish there would be better handling of transformation change done in base ProcessFormXObject method. But couldn't come up with concise solution.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PsykerUdot I had a look and could not find the reason why this is needed, maybe the problem is in PdfPig, but is not visible as it seems to mainly impact shadings.

If you have a way to create a test document relying on letters (similar to the shader based test documents you added), that would help debug in PdfPig (PdfPig does not render shadings, but if we can check the letters bounding box are properly aligned, that'd be a start)

Copy link
Copy Markdown
Contributor Author

@PsykerUdot PsykerUdot Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BobLd It is definitely not a problem of PdfPig. The code is dedicated solely to enable correct pattern coordinates mapping.
My concern is that piece of code is somewhat hacky. I thought originally to collect original transformations stack in PdfPig, but unless PdfPig processes patterns in any way it seems impractical.
It is also possible to make ProcessOperations method virtual so derived classes can handle the case in their own implementations.

@BobLd
Copy link
Copy Markdown
Owner

BobLd commented Nov 4, 2025

@PsykerUdot thanks a lot for this great contribution. I'll review it shortly

@BobLd
Copy link
Copy Markdown
Owner

BobLd commented Nov 5, 2025

@PsykerUdot I have updated branch 0.1.12 with your latest commit in PdfPig. If you want to target Skia v3, you can use 0.1.12-skia3 (also has your commit, but with the Skia v3 package). Can you change the PR to merge into one of these 2 branches?

@PsykerUdot PsykerUdot changed the base branch from master to 0.1.12-skia3 November 5, 2025 10:27
@PsykerUdot
Copy link
Copy Markdown
Contributor Author

PsykerUdot commented Nov 5, 2025

@BobLd Excellent! Switched the PR to 0.1.12-skia3. Also I have updated Tests project so new test samples are copied to the test stage folder.

@BobLd
Copy link
Copy Markdown
Owner

BobLd commented Nov 5, 2025

@PsykerUdot rthanks again for the hard work here - much appreciated!

I ran the PdfPigSkiaTest tests. Many are failing due to artifacts and can be safely ignored.

One tests shows a regression though:

Test Duration Traits Error Message
UglyToad.PdfPig.Rendering.Skia.Tests.TestRendering.PdfPigSkiaTest(expectedImage: "GHOSTSCRIPT-699488-0_1.png", pdfFile: "GHOSTSCRIPT-699488-0.pdf", pageNumber: 1, scale: 2) Failed 6.3 sec Assert.True() Failure Expected: True Actual: False

This is the expected image (zoomed in on the problem):
image

And the actual image generated from your branch (issues circled in red):
image

Some icons have disappeared... Can you have a look?

I need to add documentation on these tests but this is the high level on how they work:
If you go to bin\Release\net6.0\ErrorImages\pdfpig_skia (or any dotnet verison that's part of the tests), you'll see 2 files per test that fail:

  • xxxx_diff.png which is the difference between the expected image, and the actual rendered image
  • xxxx_rendered.png which is the actual rendered image

This is what the diff shows for this tests, showing the missing icons:
image

I hope this helps for debugging - reviewing your code changes (another failing test that might show an error - really not sure - is PDFBOX-1869-4_1_1 where it appears the shaders are translated. It might be due to the previous expect image being wrong though)

EDIT: It seems like your version of PDFBOX-1869-4_1_1 is correct, the previous was wrong

@PsykerUdot
Copy link
Copy Markdown
Contributor Author

@BobLd I'll take a look. Thank you for your feedback and clarification on how to read test results!

@PsykerUdot
Copy link
Copy Markdown
Contributor Author

@BobLd I checked it carefully and afraid a regression was introduced with UglyToad/PdfPig@e11dc6b.
I couldn't find branch 0.1.12 in https://github.com/UglyToad/PdfPig/ so I checked against "master" branch.

@BobLd
Copy link
Copy Markdown
Owner

BobLd commented Nov 7, 2025

@PsykerUdot thanks a lot for investigating! This was a bug in PdfPIg indeed - will fix shortly

@BobLd BobLd merged commit 1691e09 into BobLd:0.1.12-skia3 Nov 7, 2025
BobLd pushed a commit that referenced this pull request Nov 13, 2025
* consistent geometric transformations; fixes

* include new test samples in Tests project
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants