Hi @sfeam!
During the last weeks, I spend considerable effort on implementing a new feature for Gnuplot that I was sorely missing for many years, and that is the ability to render rasterized graph output to an arbitrary resolution, i.e. DPI. As with my last patch, I used AI (Claude Opus 4.6 and 4.7) to help with the code generation and generation of accompanying patch commentaries, but I reviewed everything the AI produced throughly. I also spended many hours testing this patch, including side-by-side comparison of the relevant new output.
The motivation for this patch, provided in raster-term-dpi-and-various-fixes.mbox is as follows: Although it is most efficient and recommended to use vector graphs for publications, etc., it is often still required by many journals or even by programs like MS Word (which historically struggled to render supported vector output like EMF in many cases) to use raster graphics at high some mandatory minimum DPI, so it was often required to take your PDF or EPS Gnuplot output and rasterize it to some required resolution (e.g. by using GhostScript, GIMP, ImageMagick, etc.). This is cumbersome, but the alternative of directly creating high-resolution output with Gnuplot involved rescaling all font sizes, line sizes, tick sizes, marker sizes, etc., manually, which is even worse.
So my idea was to introduce a backwards-compatible option for all raster terminals (libgd, cairo and webp) that allows giving the size in units of length (inches, centimeters, etc.) as already possible for the vector terminals and additionally supplying the resolution in DPI, like so
set terminal pngcairo/png/webp/etc. size 12cm,9cm resolution 300
This would of course entail that the respective terminal drivers scale everything (font sizes, line widths, etc.) correctly for the requested resolution, but it turns out this was a feasible change without too many intrusive code changes. I have taken care to make this fully backwards-compatible, so that not specifying a resolution and giving size in pixels for the raster terminals will not change behavior. I have also taken care that the resolution meta data is embedded into the output formats wherever possible (this was not the case for pngcairo and webp before).
While implementing this, I targeted the visual appearance of pdfcairo, so line widths and font sizes are scaled accordingly. Along the way, I realized that - likely for historical reasons - the actual font sizes rendered by the postscript terminal in eps mode and by the pdfcairo terminal are too small and do not match the user requested point size, i.e., when the user requests "Arial,12pt", it is really rendered as 8pt by pdfcairo. The only exception to this is postscript in non-eps mode, where font sizes are rendered at their real size. For my patch, I did not change this behavior, so fonts are rendered as they would in pdfcairo. I have a patch on-hold that introduces a new option called "realfontsizes" that would make all affected terminals render at the true font size the user requested, but I did not want to include it here, as it would make an already large patch even more complicated. If you are, however, interested in the current situation of the font scaling, I have included an AI summary at document postscript_vs_cairo_sizing.md
While implementing the DPI feature and comparing the output of png and pngcairo with rasterized versions of the same output of pdfcairo and postscript (color eps), I came across a range of unrelated bugs, for which I have included fixes in this formatted patch. For convenience, each fix is in its own commit and should not be necessary to get the main feature running, but I would highly suggest to pull at least the libgd clipping fix, since this corrects some issues that I have introduced with my previous patch that added dashtype support for libgd.
To easily test this feature, I have included a ZIP of a minimal testing script together with all of the (slightly adapted) demos and associated data files. The testing script requires ghostscript, the main feature commit and the string_result_only fix to work. It will take the DPI as an argument and produce png output for the following terminals: png, pngcairo, pdfcairo and postscript, where the raster terminals er in eps mode with size 12cm,9cm at the requested DPI and font "Arial", and the vector terminals get rasterized to the requested DPI.
I will now list the commits this patch contains in reverse order with some comments by myself, but every one of those commits also comes with a detailed mark down documents that I had the AI write and that I personally reviewed.
405e4827f Implement DPI-scaling for libgd, cairo and web raster terminals
This is the main feature implementation, it contains everything described above. The associated document is: dpi_aware_libgd_and_cairo_feature.md
1c2ef330b Fix string_result_only flag leaking into function block evaluation
This is a fix to an issue I stumbled upon when I tried to call Gnuplot functions inside set output. It is required for the attached run_dpi_test.sh script to work properly. Its associated document is: fix_string_result_only_leak.md
78283c3ed Fix Adobe glyph name truncation in PostScript terminal
This fixes an issue where Adobe glyph names for unicode characters where truncated by one character due do improper memory allocation. This lead to, for example, /pi becoming /p, which was then rendered as a Latin letter "p" instead of a greek pi. The associated document is: fix_adobe_glyph_name_truncation.md
56d442126 Fix slow QT font-metric initialization and noisy warning
This fixed a Windows issue I encountered when switching the terminal to "qt" and plotting for the first time. Under these circumstances, I always got a "Slow font initialization" warning, which probably plaques many Windows users right now. The associated document is: fix_slow_qt_font_init.md
8e90f6c5f Fix oversized Pango hex box rendering with cairo oversampling
This is a fix to a cairo issue (both pngcairo and pdfcairo) I found in the output of demos using unicode characters that are not included in the supplied font. Previously, they were rendered much to large and covered to whole graph. The associated document is: fix_pango_hexbox_oversampling.md
9848b2ad4 Fix libgd clipping and libgd/cairo border rendering issues
This fixes a libgd png terminal issue that was introduced by my previous patch that clipped too much of the border surrounding a plot. This is most visible at larger line thicknesses. It also fixes a long-standing issue where the border for png and pngcairo hat a square cutout (for thick enough lines) at the upper left corner due to closed paths not being implemented properly in these terminals. The associated document is: fix_libgd_clipping_and_libgd+cairo_border_rendering_issues.md
cb5606523 Fix mingw makefile missing DLL dependencies
This fixed an issue with missing DLL dependencies for wgnuplot.exe and wgnuplot_pipes.exe in the Windows mingw Makefile. The associated document is: fix_mingw_missing_dll_dependencies.md
00dcd312c (HEAD -> raster-term-dpi) Add Apr 2026 changelog entries to terminal source headers
Lastly, this introduces short CHANGELOG style messages at the header of touched source files that already had time (like gd.trm). However, they summarize changes for all the commits above, so if you want to use them while not picking certain commits, they likely need to be adapted. I was not sure whether you wanted those messages at all, so I included them in their own commit.
The commits listed above all include a longer commit message that you could directly use when merging. I tried to keep it short but, as it is know, they might still be a little too detailed for your taste. Let me know if you want them shorter.
I realize that this is a lot of content, and since an AI was involved, you are probably sceptical about the quality. I can assure you, however, that these fixes have all been hand-vetted by me and tested over several weeks until I arrived at this point. This is still no guarantee that they are free from errors, but I hope that the code quality is good enough for you to consider merging these patches or at least most of them.
I have included some demo output at various DPIs for you to gain an impression of what this patch would make possible.
Cheers!
PS: I have forgotten to upload the 3dboxes plots for 50 and 300 dpi, here they are. Let me know I you want further test output like the one posted above. You can generate it yourself with the test scripts I have provided, but I can also upload it somewhere. I have 888 plots available that were produced by the demos included with Gnuplot.
PPS: Sorry for all the typos in the original ticket, it's been a long day and it seems I can't edit them out.
I think I am learning a bit about the good and bad of LLM-assisted
patch evaluation. This set of patches falls into several categories.
I will try to sort the contributions here accordingly, but let me start with one that doesn't work for me.
[PATCH 5/8] Fix oversized Pango hex box rendering with cairo
Does not work in my testing. The huge missing-glyph boxes are gone but
instead I get a mess of what looks like the corners of still large,
off-screen empty boxes. This really looks like a pango bug and not
something we should have to fix in gnuplot.
Before and after rendering in pdfcairo attached below
Last edit: Ethan Merritt 2026-04-24
Hi @sfeam,
did the patch work for you for
demo/armillary.demanddemo/charset.dem? This is where I found the issue and this patch fixed it for me, at least for these two demos.From what the LLM claimed and what I just double-checked, the error indeed comes from Pango's
pango_cairo_hex_box_render_glyphinpango/pangocairo-font.c. There, upon rendering a hex box for a glyph not found in the current font, the transformation matrix is reset to the identity matrix:cris later restored, but the damage when rendering the hex box without using the scaled down 1/200 matrix that Gnuplot's cairo terminal uses internally is already done.Now the question is, whether this really is a bug on Pango's side (seem like it to me), or whether this matrix-resetting has some internal reasons.
If you think it is a bad idea to try to work around this bug in Gnuplot, we do not have to merge this, especially since there seem to be problems on your side.
If you think it is worthwhile fixing it: Can you send me the script that fails for you so that I can investigate this further?
armillary.demworks for me without any missing glyphs, so the patch is not relevant. I.e. the glyph/font support provided by some underlying library (in my case libfontconfig) is also a contributing factor.charset.demdoes indeed show that the patch removes over-large garbage from the output; note, however, that since the entire point of the demo is to determine what can or can not be printed with the current settings a better solution might be to simply skip the unavailable characters altogether rather than dealing with improperly rendered replacement glyphs. See proposed alternative approach below.I figured out that the garbage output I saw before is because my script requests boxed text. The patched code suppresses the bad glyph rendering but still reports text metrics that are off by a factor of 200. That produces a box around the text drawn as a huge rectangle with the wrong center. The incorrect centering is true even for unboxed text, so I suspect the patch results in incorrect placement for all right- or center- justified text longer than a single character.
I definitely think this is a pango bug and that is where it should be reported and hopefully fixed.
I wonder if a different work-around is possible in gnuplot that would be simpler and possibly more generic. The current patch uses
pango_layout_get_unknown_glyphs_count()to detect the error case. If this routine can be used iteratively to drill down and find individual problem characters, then gnuplot can simply delete that character from the output text or replace it with what Unicode calls a ".notdef glyph". Unfortunately there is no Unicode code point for this symbol. Some rendering environments use � (U+FFFD REPLACEMENT CHARACTER) instead. That is what you see in the qt output I attached. Other possibilities are ▯ or ⍰ or a blank space.Ah, you are probably using a different fallback font that has all the glyphs of
armillary.dem, while my fallback font was missing a glyph.The boxed text you requested in your script indeed poses a problem for the current patch. Your suggested alternative approach sounds feasible, I will try to come up with a workaround that involves this strategy.
Hi @sfeam,
your suggestion of using deleting problematic characters from the output text was the right idea. I have implemented this now using blank spaces, as they can be used in the enhanced text path without changing byte offsets in
PangoAttrList. If multi-byte glyphs are encountered, they are overwritten with the appropriate amount of spaces. This will likely make the text wider in many instances, but I think this would be an acceptable workaround.Your other suggested replacement glyphs are all more than 1-byte long and would not work, at least not for the enhanced text path. Also, they also might not be available for all fonts.
I have rebased this modified fix on master and attached it In case you want to merge it as a workaround until the Pango bug is fixed.
Hex box fix #
Unfortunately this patch does not work for me. The presence of a missing glyph seems to clobber the whole line of text, not just that one character. This is what I get from
charset.demon the wxt terminal. Notice how the entire lower-case alphabet is missing? That is because the final 2 characters in the string are 0177 and and 0200. Both codepoints are unused in Unicode. 0177 is actually ASCII DELETE, so it is meaningful in some circumstances but still has no glyph.I am guessing that one of the earlier calls into pango needs to be redone after changing the text, but I have not figured out which one it would be.
[followup] maybe a clue: the line of text in charset.dem label 4 only disappears in enhanced text mode. If I set label 4 to "noenhanced", it works.
My apologies, I had looked at the output of charset.dem, but I failed to see that the line with lower case letters was just completely gone.
I would not have found this issue myself so quickly, but the LLM, after going through the Pango source, found it. I had to intervene a little with the fix because the LLM tried to repeatedly call
gp_cairo_replace_missing_glyphsat the various places wherepango_layout_get_extentswas called, but otherwise, the suggested fix seemed fine to me.The final LLM summary on the fix was:
The issue was that
gp_cairo_enhanced_flush()builds up to five intermediatePangoLayouts (for save/restore, overprint, hide, zero-width, and the showflag-false branch) and queries their extents. Those extents are converted intoPangoAttrShapewidths inserted into the maingp_cairo_enhanced_AttrList. When any of those layouts contained an unknown glyph, Pango's hex-box bug returned a hugely inflated width, so a giant invisible "shape" got injected into the final layout and pushed all the visible text off-canvas — making the whole label look blank in enhanced mode (but fine with noenhanced, exactly as you observed).Fix in
gp_cairo.c:Added a small wrapper
gp_cairo_layout_get_extents()that pairs the missing-glyph substitution withpango_layout_get_extents()so callers cannot accidentally read pre-substitution (hex-box-inflated) extents.Replaced all eight replace_missing_glyphs + set_text + get_extents triples — in
gp_cairo_draw_text(),gp_cairo_enhanced_finish(), and the five intermediate layouts insidegp_cairo_enhanced_flush()with a single call to the wrapper.OK. Very good. I have not found any test cases that this version fails to handle.
I replaced the in-line decoding of UTF-8 sequences with the existing macro
advance_one_utf8_char. I think this make it a bit more readable but does not change the logic at all.committed in 6.1
1) Simple fixes for simple errors
[PATCH 3/8] Fix Adobe glyph name truncation in PostScript terminal
good:
The patch to post.trm corrects a string length error introduced by an
earlier commit that replaced strncpy with safe_strncpy in many places,
but got the length wrong (entirely my fault).
The fix is obviously correct. I've applied it. Thanks.
bad:
The analysis of where the bug came from is wrong. It is not a
"long-standing bug present since the UTF-8/glyphshow support was
added in 2007 (Thomas Henlich, Sep 2007)".
Instead it came from my error in commit
96d580c5 2024-07-25 more consistent use of safe_strncpy()
This pattern of getting the length wrong occurred in three places
in that commit; the other two were subsequently caught and fixed.
So all is good now, but the LLM synopsis would not have led to
finding and fixing the other two places had this one been found first.
[PATCH 2/8] Fix string_result_only flag leaking into function block
good:
The patch and analysis are correct.
I've applied it. Thanks.
[PATCH 6/8] Fix libgd clipping and libgd/cairo border rendering
good:
Addition of cairotrm_path() for png and kitty terminals
looks correct to me.
The change to boundary() looks OK.
I will break these out and apply them separately.
bad:
The gd clipping stuff looks extensive and complicated, and should
not be mixed in with the cairo changes. I take it this is a fix
for a problem introduced by your earlier gd antialiasing commit?
I'm setting this one aside for now to look at later.
It's not in the "simple fix for simple problem" category.
[PATCH 7/8] Fix mingw makefile missing DLL dependencies
good:
Looks simple, provided that the modified commands run successfully
using mingw. I can't test this. Can you confirm it works?
Last edit: Ethan Merritt 2026-04-24
Regarding your comments on the Adobe glyph patch
Yeah, the LLM clearly tripped up here about when this issue was introduced. It probably saw a comment in the source's changelogs about when UTF-8/glyphshow was first introduced and hallucinated that this was when the bug was introduced. Carefully going over all commits (or being involved with the project for a longer time) would have avoided this, but I would probably have easily made the same mistake. I am not an AI apostle by the way, I hold the same view as Linus Torvalds, that they are useful tools.
Regarding your comments on the libgd clipping fix
The clipping optimizations/fixes for the libgd path indeed fixes some minor performance and rendering issues I have introduced myself with the previous libgd-dashtype-patch.
The whole reason for introducing clipping in the plot area in the first place was during my tests with thick dashed lines and various endcaps, I realized that, especially slanted lines stick out of the plot area. This looked ugly and in extreme cases, the parts that stick out can even overlap with the tick numbers. So I introduced clipping as an easy fix, but later realized, that the clipping:
a) Is done on every call of PNG_Vector(), which is needlessly expensive
b) Will clip thicker plot border lines (I saw this in colorscheme.dem) on the inside.
So the libgd part of this fix should take care of both the performance issues and the parts that were improperly clipped. Unfortunately, clipping in a way that leaves the plotting border intact is also significantly more complex and thus the code changes are not trivial.
That being said, none of the other terminals support this kind of clipping right now (although I would prefer they did and would also be willing to provide a patch for them as well), so an alternative would be to just remove clipping in the libgd terminals altogether to avoid this asymmetry.
I have meanwhile also found that the libgd clipping update of this patch does not yet work properly in some cases, so do not merge that yet. I will provided an updated patch that also splits the libgd clipping update from the rest of the changes.
[PATCH 6/8] Fix libgd clipping and libgd/cairo border rendering
I have extracted and committed the parts of this patch that add term->path to the cairo terminals and gd terminals. Thanks.
Perfect. I will put some more work into the libgd part and provide two options: One that completely removes the clipping (which would make it symmetric to the cairo terminals) and one that properly clips with a layer based approach (such that clipping is not done any longer on every
PNG_Vectorcall.Hi @sfeam,
as promised, I have prepared two patches now regarding the plot area clipping in libgd terminals previously introduced by my dashtype support commit:
One uncomplicated patch that just removes the plot area clipping completely, thus making libgd symmetric again in behavior to all the other terminals that also do not clip the plot area. Beware, however, that this can - for thick lines or points - look pretty ugly, as evident by the attached examples, where the Cairo output is unclipped (as it always was) and the libgd output is clipped.
One more complicated patch (and an accompanying patch commentary) that introduces a fixed version of the
PNG_layer()callback per-curve clipping approach. This produces very nice clipped outputs with very low performance overhead. If you think this is worth merging, I could look into how to get this into the Cairo terminals as well.EDIT: Forgot to attach the patches.
Last edit: Sethur 2026-04-28
Nearing the end line
I have applied the "no clipping" patch for now.
I actually do like the look of the clipping, but I I would prefer a slightly different implementation that does not tie the clipping to TERM_BEFORE_PLOT / TERM_AFTER_PLOT. Why? Because clipping to the plot border only makes sense if there is a plot border. Consider the following example (more or less taken from polar.dem. (see attached script and output)
There is one other aspect of the current implementation that I dislike because I consider it a layer violation. If possible, terminal drivers should not reach back into the core code to retrieve data structures. Yeah there are probably some bad examples in older code, but I'd really like to avoid having gd.trm refer directly to
gadgets.h: BoundingBox *clip_areaI propose a small tweak to your current implementation that takes care of both these issues.
void clip_state(BoundingBox *clip_state, int state)stateparameter just on the chance it proves useful in the future. Maybe for other terminals? It might, for example, be used to indicate what graphics operations do or do not get clipped.What do you think? I hope I didn't miss some wrinkle that makes this more complicated.
Hi @sfeam,
your approach is indeed much better than what I had initially proposed. It would also make it easier and clearer to implement clipping for cairo terminals.
I have integrated your proposed changes into the patch and am currently testing it thoroughly. I will post it here if the tests show no issues.
While testing, I have found a minor bug regarding my dashtype/antialiasing patch. There is a condition (very thick lines above lw 32 and certain sampling rates) at which small artifacts (missing pixels) may appear. This is because under certain conditions, the disk joins are not drawn when they should have been drawn. I will post a small patch for this here as well, unless you specifically want me to post this in a new thread.
Hi @sfeam,
as promised, please find attached the updated clipping fix. I have tried to integrate all your suggestions and tested this thoroughly by reviewing the output of all demo scripts manually and also benchmarking it against older builds.
The patch consists of two commits because I wasn't sure you wanted the additional clipping calls in place_objects(). Without them, the libgd SDF rasterizer (
gd.trm) will produce some minor visual artifacts in the output of script likeclipobject.demwhere the clipped outer frames of the rendered shapes (ellipses, triangles, etc.) can bleed through the clipping area, but this is nothing major and artifacts where already present in this demo output for libgd before the dashtype support patch.This patch also contains a performance fix for the libgd SDF rasterizer (in
PNG_draw_aa_thick_line()) that rejects pixels that would have been rejected anyway byPNG_SetAAPixelColor()call togdImageBoundsSafe()I have included a summary (originally created with LLMs but manually reviewed) for the patch and the benchmark results as well. The benchmarks were run on WSL (Windows Subsystem for Linux) with Ubuntu 24.04 LTS. They mention png, pngcairo, pngorg and pngcairoorg, where the latter two are the results from a build just before the patch introduced with #833, i.e., one commit before 423698592.
EDIT: The benchmark results contained a duplicated table => fixed.
I have tested and benchmarked this together with three other smaller fixes that are all minor patches to issues I have introduced myself with the previous dashtype-antialiasing-libgd-patch (patch #833).
For these minor fixes I will, however, open a new thread, since they don't fit here topic-wise.
Last edit: Sethur 5 days ago
2) Somewhat complicated changes that may fix a known problem
[PATCH 4/8] Fix slow QT font-metric initialization and noisy warning
good:
I will be delighted if you can help clear some of the known
Windows-specific issues, with or without LLM assistance.
From the comments I take it that this commit may fix open bug
2316 "Slow font init" with every first qt plot in a session (Windows)
and possibly also
2476 font problem on qt terminal on windows
bad:
The LLM analysis does not find or at any rate does not mention the bug
reports that this change may fix. Since in general the original
reports may include reproducers or other relevant details, it would
be very helpful if this were part of the summary report.
If nothing else, it would be useful so that the bug report can be
closed if this fixes it.
Unfortunately I can't test for myself whether this commit does resolve
these known issues because I'm not set up to compile or run under Windows.
Can you confirm that these compile and build correctly under Windows
and do indeed fix bug 2316?
Also that a 3-second grace period is sufficient on an older/slower machine?
I have a Windows system on hand and can check whether this patch will fix the bugs you mentioned. I will post here again if I have an answer.
Hi @sfeam,
I have:
Feel free to increase the Windows grace period even further. I thought if it takes longer than 5 seconds, it's warranted that the warnings is shown.
EDIT: Replaced the patch file, the previous one did not include the changes listed above.
Last edit: Sethur 2026-04-28
PS: I am confident that this solves bug #2316 , since this bug describes exactly the warning message problem that this patch is trying to solve.
I've applied it thanks.
I would have liked to get confirmation is from someone who can say "Without this fix my Windows machine shows the problem; after this fix the same machine does not show the problem". But I realize your machine may not show the problem in the first place, in that which case you can't be 100% sure that the change has fixed it, only that it ought to have fixed it. We'll have to wait and see whether one of the original bug reporters notices and comments.
Well, my Windows machine definitely does show the issue, i.e., I get the
slow font initializationwarnings. And on my machine, this patch worked as intended, i.e., the warnings never showed again, even on cold caches.However, there might still be Windows machines out there where the current grace period of 5 seconds is not enough.