Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses issue #227 by preventing unintended Rplots.pdf file generation when graphics::strwidth() and graphics::par() are called without an active graphics device. The solution introduces a helper function that manages graphics device lifecycle by opening a null-output device only when needed and ensuring it's closed on exit.
Key changes:
- Introduces a
with_graphics_device()helper that wraps graphics function calls to prevent device leaks - Refactors all
graphics::strwidth()andgraphics::par()calls in R code and tests to use the new helper - Removes
Rplots.pdffrom.gitignoresince it should no longer be generated
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| R/safe_graphics.R | New file implementing device lifecycle management helpers: with_graphics_device(), open_null_device(), close_device(), and file_null() |
| R/rtf_strwidth.R | Updated to wrap graphics::par() and graphics::strwidth() calls with with_graphics_device() |
| tests/testthat/test-independent-testing-rtf_strwidth.R | Updated test code to wrap graphics::strwidth() calls with with_graphics_device() |
| tests/testthat/test-independent-testing-rtf_nrow.R | Updated test code to wrap graphics::strwidth() calls with with_graphics_device() |
| .gitignore | Removed Rplots.pdf entry (no longer needed) and sync_bitbucket.R (appears unrelated) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Cool, thanks for fixing the issue! |
yihui
left a comment
There was a problem hiding this comment.
I need more time to understand the rtf_strwidth() function and will review the PR tomorrow.
|
Just to make sure I understand pdf(NULL)
on.exit(dev.off())in |
Unfortunately, a quick test shows that I was wrong: > png()
> strwidth('hello world', 'inches', family = 'Times')
[1] 0.7637533
> dev.off()
> pdf()
> strwidth('hello world', 'inches', family = 'Times')
[1] 0.7621667
> dev.off()That means |
|
BTW, although I don't think any of you would consider switching to another testing framework, I'd like to mention that I also ran into this problem several years ago and provided an option in {testit} to automatically clean up new files generated from tests (including this |
|
Yes, it seems clear that the original intention for And yes, the current output is dependent on the graphical device context and thus unpredictable. The goal of this PR is to eliminate the annoying Theoretically, calculating this should NOT require a graphical device. The parameters that truly matter are the font, font size, and DPI. See a proper solution in rtflite (Python) calling Pillow's Here is a table comparing these possible solutions:
|
|
I'm merging this as this seems to be the optimal solution at the moment without moving mountains. |
|
After thinking a bit more on this, I feel we should make If |
|
That would be an improvement and a good vibe coding project. Besides predictable results, if we also care about correct results, we can combine the systemfonts/FreeType core facility and calculate them with the mapped metric-compatible fonts in rtflite. |
Fixes #227
This is new try (after PR #228) to fix the issue reported in #227 where calls to
graphics::strwidth()andgraphics::par()unintentionally generateRplots.pdffiles in some environments due to opening a new default graphics device that is never closed.The discussion in that PR was very helpful in getting this "more proper" solution.
with_graphics_device()helper to rungraphics::strwidth()/graphics::par()without implicitly leaving the default device open by opening a null-output device only when needed and closing it on exit.R/andtests/to use the helper.Performance comparison:
Note that changing this might affect some
strwidth()calculation results slightly.