Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fill hatch in PDF backend #29392

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

r3kste
Copy link
Contributor

@r3kste r3kste commented Dec 31, 2024

PR summary

closes #10034

Summary

Adresses an issue specific to the pdf backend, where hatches such as stars and circles were not filled, which is unlike the other backends such as Agg or svg.

Proposed Fix

Although Op.fill_stroke is used for the hatch path (which should make the hatch path filled) the non-stroking color is not equal to the hatch_color (which previously, was only applied as the stroking color)
This PR fixes this by setting the non-stroking color as the hatch_color.

Changes to Baseline PDFs

I have updated the baseline PDFs for the tests that are failing, although I am not sure what exactly is causing the tests other than test_artist.py::test_clipping[pdf] to fail. The expected and generated PDFs are identical, but the corresponding PNGs are not. It looks like the lines are thicker in the corresponding PNGs.

Additional Info

Essentially the fix should apply to only hatches that need filling (like stars and circles) which would result in changing only one baseline PDF, i.e. test_artist.py::test_clipping[pdf].
We unfortunately cannot do this, because path for drawing all the hatches are generated in advance and we need to draw all of them together.
As for the other tests, Filling lines may seem like there should be no difference, but the PDF backend does some shady stuff by thickening them in the corresponding PNG. This needs to be investigated upon.

Before After
test_artist.py::test_clipping Screenshot_20241231_224900 Screenshot_20241231_224959
test_legend.py::test_hatching PDF Screenshot_20241231_225111 image
test_legend.py::test_hatching Corresponding PNG image image

Diff for the last row:

hatching_pdf-failed-diff

PR checklist

@r3kste
Copy link
Contributor Author

r3kste commented Dec 31, 2024

It is also worth noting that, previously (in main) for test_backend_pdf::test_hatching_legend the expected and generated PDFs were different, but the corresponding PNGs are same. So the ImageComparision passes, although the PDFs are dissimilar.
It looks like the expected PDF is generated with hatch_linewidth as $0$, while the actual PDF is generated with hatch_linewidth as $1$.

Expected Actual
PDF image image
Corresponding PNG image image

@oscargus
Copy link
Member

the PDF backend does some shady stuff by thickening them in the corresponding PNG

Yeah, there is something strange going on, see, e.g., #24263 which has a similar problem.

For the PR, I think it makes sense, but wouldn't it be possible to conditionally execute the added line based on which hatch is created? Or am I missing the point of the for-loop?

@r3kste
Copy link
Contributor Author

r3kste commented Jan 24, 2025

but wouldn't it be possible to conditionally execute the added line based on which hatch is created? Or am I missing the point of the for-loop?

I think this isn't possible, because of certain cases where the hatch could be a combination of marker and line hatches, for instance */.
It looks like the for-loop in writeHatches iterates through the hatchpattern of different objects.

@r3kste
Copy link
Contributor Author

r3kste commented Mar 21, 2025

@oscargus @jkseppan I would like to know your thoughts on this PR.

@story645 story645 mentioned this pull request May 16, 2025
6 tasks
@r3kste r3kste force-pushed the pdf_hatch_fill_patch branch from 6000941 to c42ea46 Compare May 16, 2025 18:57
@jkseppan jkseppan self-requested a review May 17, 2025 02:48
@r3kste r3kste force-pushed the pdf_hatch_fill_patch branch from c42ea46 to c54d7a5 Compare May 17, 2025 06:18
@jkseppan
Copy link
Member

It is also worth noting that, previously (in main) for test_backend_pdf::test_hatching_legend the expected and generated PDFs were different, but the corresponding PNGs are same. So the ImageComparision passes, although the PDFs are dissimilar. It looks like the expected PDF is generated with hatch_linewidth as 0 , while the actual PDF is generated with hatch_linewidth as 1 .

Looks like the baseline images were changed in #29908, so this is no longer the case. The linewidth has been set to 1 since #28048, so you shouldn't see this difference any longer.

The reason that linewidths of 0 and 1 look similar when compared is probably because we do the rasterising without antialiasing (something like the -dTextAlphaBits=4 -dGraphicsAlphaBits=4 options to GhostScript) and a linewidth of 0 means "as thin as possible on the output device". It's been that way since 8c60d5c, and I think the reasons were performance concerns and that it sometimes hides small font differences, when you have the same font metrics but slightly changed fonts (which does happen at least with TeX fonts). Since then we've removed text from most baseline images, and perhaps the performance concerns are not that important, as the tests take a long time in any case. We might want to revisit the way the comparison is done.

@jkseppan
Copy link
Member

jkseppan commented May 18, 2025

This causes various line-based hatches to look thicker, depending a little on the renderer used. The hatches have always been rendered in PDF with a fill+stroke operation, but now that there is a fill color in all hatches, the lines get "filled" which adds some pixels.

Would this cause hatch patterns 'o' and 'O' to be filled? They are documented in hatch.py to be unfilled (and '.' is a small filled circle, and it does not seem to be documented whether '*' is filled or not).

@jkseppan
Copy link
Member

Would this cause hatch patterns 'o' and 'O' to be filled? They are documented in hatch.py to be unfilled (and '.' is a small filled circle, and it does not seem to be documented whether '*' is filled or not).

It doesn't! The circle paths are drawn forward and backward when the circles are not meant to be filled, presumably for this exact reason.

That leaves the "filled" lines which get extra pixels. This is only a problem at low resolution and don't seem to be an issue when viewing with a PDF viewer, so perhaps not worth fixing. The fix would likely involve changing hatch.py to return multiple paths along with "stroke" and "stroke+fill" data, instead of the current single noncontiguous path that gets stroked and filled.

@jkseppan
Copy link
Member

If we add antialiasing and increase the resolution, only clip_path_clipping.pdf would need to be changed and the other tests would pass with the baseline pdfs in main.

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

Successfully merging this pull request may close these issues.

Hatching is rendered differently by agg, pdf and svg backends.
3 participants