Skip to content

fix(deps): update dependencies - #41

Merged
tannevaled merged 1 commit into
mainfrom
renovate/deps
Aug 31, 2026
Merged

fix(deps): update dependencies#41
tannevaled merged 1 commit into
mainfrom
renovate/deps

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
github.com/go-gfx/gfx v0.11.0v0.19.0 age confidence
github.com/go-widgets/mvvm v0.8.0v0.9.0 age confidence
github.com/go-widgets/painter v0.11.0v0.12.0 age confidence
github.com/go-widgets/toolkit v0.284.0v0.298.0 age confidence
github.com/go-widgets/window v0.20.0v0.57.0 age confidence

Release Notes

go-gfx/gfx (github.com/go-gfx/gfx)

v0.19.0

Compare Source

v0.18.0

Compare Source

v0.17.0: — a TIFF that is not eighteen times too big

Compare Source

tiff.Encode was passed nil options, which reads like "no options" and means "no compression". On a page-sized picture that is 7 755 458 bytes against 415 493 with Deflate — eighteen times.

It was found downstream. go-pdfkit/app added a format chooser for handing a drawn page over and measured what each costs over 40 real documents: GIF 105 kB, JPEG 129 kB, PNG 209 kB — against BMP 4.9 MB and TIFF 6.6 MB. The ratio between those two is exactly four bytes a pixel against three, the alpha channel TIFF carries: both were the raw raster.

Deflate is compression 8, which every reader written this century takes. LZW would be the other candidate and this library cannot write it — tiff.Encode refuses it.

BMP stays large, and now says so. BMP has no compression to ask for; the documentation says it rather than leaving somebody to find out on a document of two hundred pages, and a test asserts it, so a BMP encoder that one day learns to compress makes what is written here fail rather than merely become untrue.

The compressed file is read back and compared pixel for pixel. A smaller file nothing opens is worse than a large one.

100% statement coverage, go vet and -race clean, nine cross-compile targets, CGO-free.

v0.16.0: — the registry writes too

Compare Source

codec.Encode writes PNG, JPEG, GIF, TIFF and BMP, each through the same reference library that reads it. Nothing here encodes anything itself.

The registry could read eleven formats and write none, so every consumer that had a picture and needed a file wrote its own encoder call — and a PDF toolkit that can draw a page could not offer "PDF to PNG".

Reading and writing are not symmetric, and the gap is not an oversight: a reference decoder exists in pure Go for every format Sniff names, a reference encoder for five of them. WEBP, ICO, ICNS, PNM, QOI, JP2 and JBIG2 return ErrCannotEncode rather than something in another format under the asked-for name. CanEncode says which way a format goes without having to fail to find out.

Alpha survives into PNG and TIFF. The other three do not, and what they are given is the image composited onto white — chosen here rather than left to the encoder, because an encoder that simply drops the channel puts the colour that was under the transparency into the file, and for a page drawn on transparent ground that is black.

100% statement coverage, go vet and -race clean, nine cross-compile targets, CGO-free.

v0.15.0: — a JBIG2 decoder that reads real scans

Compare Source

The JBIG2 decoder now comes from a fork carrying one fix, offered upstream as dkrisman/gobig2#2.

Upstream's per-symbol pixel cap defaults to 4 megapixels, on the reasoning that a real glyph is "tens of pixels per side". Real scanned documents disagree: some encoders emit a page-sized region as one symbol. Of 403 JBIG2 streams taken from the /Mask and /SMask entries of public Internet Archive scans, 7 are refused at that default — they need between 7 and 8 MP for a single symbol, and poppler reads all of them at its own defaults.

A consumer could not simply raise it: the limits are process-global variables, so a library that raised them would raise them for everything else in the binary.

The fix defaults the per-symbol cap to the aggregate cap, which is charged for every symbol three statements later and so already bounds the work — a per-symbol cap below the aggregate forbids a shape, not an amount of work.

With it the same 403 streams decode 403 of 403, every one bit-exact with poppler's own decoder, compared by extracting the image rather than rendering the page.

The fork also ends this module's dependency on an untagged one. It is meant to end: the day upstream tags a release carrying the fix, this goes back.

100% statement coverage, go vet and -race clean, nine cross-compile targets, CGO-free.

v0.14.0: — the JBIG2 a container embeds

Compare Source

codec.DecodeEmbeddedJBIG2 reads the headerless form of JBIG2 — segments and nothing else, with the shared ones handed in separately — which is what a PDF stores in a /JBIG2Decode stream.

v0.13.0 added JBIG2 as a file. The embedded form carries no signature and its globals cannot be guessed from the stream, so it cannot be sniffed and sits outside Decode's contract on purpose.

It is here so that one package names the JBIG2 decoder. That matters more than usual for this format: the reference decoder's resource limits are process-global rather than per-decode, so a library cannot raise them without changing them for everything else in the binary, and it publishes no tagged version.

Globals that make no sense do not stop a stream that does not read them — which is nearly all of them. Of 403 JBIG2 streams in a corpus of scanned documents, none named a globals stream.

100% statement coverage, go vet and -race clean, nine cross-compile targets, CGO-free.

v0.13.0: — JBIG2, the ink layer of a scanned page

Compare Source

codec reads JBIG2, the format a scanned page's ink is stored in. A modern scan is two images — a low-resolution colour background as JPEG 2000, and a high-resolution bitonal ink layer over it as JBIG2. v0.12.0 could read the background; this reads the ink.

No decoder is reimplemented, as for every other format in the package. Choosing the reference was the work: four pure-Go candidates exist and all four advertise full segment coverage. 403 JBIG2 masks were extracted from real scanned documents and every pixel compared against poppler's own decoder.

decoder decoded identical to poppler
dkrisman/gobig2 396/403 396 — 100%
jdeng/gojbig2 399/403 37 — 9%
dobbo-ca/byblos 157/403 157 — 100% (generic region only)
xiaoqidun/jbig2 0/403

The candidate that decodes the most is wrong nearly everywhere. Counting successes would have picked it; comparing pixels is what did not.

The headerless form a PDF embeds carries no signature and is deliberately not sniffed — a PDF consumer learns the format from the image dictionary. TestTheEmbeddedFormCarriesNoSignature holds that line.

100% statement coverage, go vet and -race clean, nine cross-compile targets including big-endian s390x, CGO_ENABLED=0.

v0.12.0: — JPEG 2000, which is what a scanned page is stored in

Compare Source

JPEG 2000 — which is what a scanned page is stored in

This package reimplements no decoder. It sniffs a container and hands it to a pure-Go reference. JPEG 2000 was the one shape it did not know, and that mattered more than anyone had measured.

Extending the corpus to where a scanned page actually lives says so plainly. Per population, the share of documents carrying a JPEG 2000 image, and the pages that have nothing else on them at all:

population documents pages blank without it
biodiversity scans 250 of 250 (100.0%) 310
medical heritage 248 of 250 (99.2%) 278
scanned books 144 of 222 (64.9%) 67
court filings 2 of 250 (0.8%) 0
government forms 4 of 450 (0.9%) 0

The last two rows are why this was nearly not done. Measured over forms alone, JPEG 2000 looks like eleven files in sixteen hundred and not worth a decoder. That figure was about the corpus, not about the world.

Not written here, and not written at all

github.com/ajroetker/go-jpeg2000 is 20 050 lines of pure Go — EBCOT, the wavelet transforms, the MQ coder, tag trees, packets, both the JP2 container and the bare codestream, decoder and encoder, with its own conformance suite and no cgo. Writing a second one would be the reference-library rule broken in its plainest case.

Checked rather than assumed:

  • it builds for all nine targets this fleet ships, js/wasm and s390x included, despite carrying a SIMD dependency;
  • it decodes 1 610 of 1 610 real JPEG 2000 images taken out of eighty scanned books, with no refusals.

Two shapes, one picture

A JP2 file opens with the twelve-byte signature box. A PDF's /JPXDecode stream is usually the bare codestream, which opens with SOC immediately followed by SIZ. Sniff knows both, because in the wild almost every scanned page is one of them.

A test that was wrong about the format

It asserted that half a codestream is refused. It is not, and should not be: JPEG 2000 is progressive by construction, so a codestream cut short is a coarser picture of the same size — the opposite of a Flate stream cut short. A decoder that refused it would throw away a page every other reader shows. The test now says that, and a separate one says that something which merely opens like a codestream and continues into nonsense is still refused.

100% statement coverage, go vet and -race clean, nine cross-compile targets.

go-widgets/mvvm (github.com/go-widgets/mvvm)

v0.9.0

Compare Source

go-widgets/painter (github.com/go-widgets/painter)

v0.12.0

Compare Source

go-widgets/toolkit (github.com/go-widgets/toolkit)

v0.298.0

Compare Source

v0.297.0

Compare Source

v0.296.0

Compare Source

v0.295.0

Compare Source

v0.294.0

Compare Source

v0.293.0

Compare Source

v0.292.0

Compare Source

v0.291.0

Compare Source

v0.290.0

Compare Source

v0.289.0

Compare Source

v0.288.0

Compare Source

v0.287.0

Compare Source

v0.286.0

Compare Source

v0.285.0

Compare Source

go-widgets/window (github.com/go-widgets/window)

v0.57.0

Compare Source

v0.56.0

Compare Source

v0.55.0: — Config.Passive

Compare Source

Config.Passive makes a window a picture and nothing else: it never becomes key, it ignores mouse events, and the application is made an accessory so it cannot steal the keyboard by appearing.

For a viewer — a head-up display, a surface inside glasses — taking input is not a feature but a trap: the keyboard leaves whatever the person was typing into, and the pointer that wanders onto the display it owns is invisible, because the picture is a capture of somewhere else. Measured on a pair of glasses; the way out was unplugging them.

A passive window is driven from outside instead: a global shortcut, a menu-bar item, a socket.

v0.54.0: — Config.FixedSize

Compare Source

Config.FixedSize makes a window unresizable: no resize control, no drag on an edge, no zoom button. For a window sized to its own content there is nothing to gain from resizing it and something to lose — room has to be found for what a smaller window cannot show, and the honest answers are a scrollbar in a dialogue that never scrolls or a reflow into a layout nobody designed.

Resizable stays the default. Honoured by the macOS back-end; accepted and ignored elsewhere.

v0.53.0: — the system font, before the window

Compare Source

window.SystemFontTTF() is now package-level, so an application can install the host's UI face before it lays out the window whose height depends on it: the font decides how tall a line of text is, so it decides how tall the window has to be, and reaching the old method meant opening the window first. AppearanceReader.SystemFontTTF keeps its contract and forwards to the same code, so the two seams cannot drift.

macOS reads /System/Library/Fonts/SFNS.ttf, Windows the Segoe UI file; Linux and js/wasm report the same error as the capability, because a Linux desktop names a font family and leaves finding it to fontconfig.

v0.52.0

Compare Source

v0.51.0: — a full-screen window went to the display the desktop USED to have

Compare Source

Open with a chosen display and Fullscreen could put the window on the WRONG
panel, in a window, and report success. It was found by an application whose
operator said "I see it on my main screen, not in the glasses" — and then "in a
window".

+[NSScreen screens] is a cache

AppKit fills it on first read and refreshes it only when a running
NSApplication processes a display reconfiguration. A process that has not started
one — which is every process on its way in, listing displays to choose between
them — reads the arrangement as it stood at that first read, for ever. Measured
after three virtual displays were created:

NSScreen        2 displays, "VITURE Beast" at (-1920,0)
CoreGraphics    5 displays, "VITURE Beast" at (-7680,0)

Nothing looked wrong, and that is the worst part. Every reading agreed with every
other, so a caller waiting for the arrangement to "settle" settled instantly on a
false answer. FindScreen then matched a stale value against a stale list and
SUCCEEDED — a stale value looked up in a stale list matches — so no error was
ever raised. The existing live test passed because it lists the displays and
opens the window in the same breath, which is the one order a real application
never uses.

What changed

Geometry now comes from the window server. CGGetActiveDisplayList,
CGDisplayBounds and CGMainDisplayID, in pure Go over purego. AppKit is asked
only for what is properly its own — the name, the backing factor, the menu-bar
and Dock inset — keyed by CGDirectDisplayID and never by a rectangle.

Creating a window brings AppKit's cache up to date first, because AppKit must
still agree which display a rectangle is on; otherwise -[NSWindow screen] is
nil and the backing factor is borrowed from the main display, sizing the
framebuffer for the wrong panel. What moves the cache is not obvious: turning the
run loop does nothing, with or without an activation policy or
-finishLaunching. What moves it is asking NSApp for an event —
-nextEventMatchingMask:, PEEKED rather than dequeued — after which it lands
within one turn. Thirteen milliseconds, waited for rather than slept through.

ScreenInfo no longer has an unexported nativeFrame. The AppKit rectangle
is derived from the exported fields, so a value rebuilt by a caller places
identically to an enumerated one. A test enforces the absence by reflection, on
any machine with nothing attached: the trap is now impossible rather than
avoided.

The non-fullscreen path was wrong twice — it shared the stale origin, and it
put the window's CONTENT at the display's top edge, which puts the title bar past
it. AppKit will not leave a title bar where nobody can grab it, so it moved the
whole window to the main display. The frame is now placed at the usable top-left
after creation, once AppKit can say how tall its chrome is.

FindScreen returns an error, keeping "unplugged" apart from "could not read
the display list". A window that quietly opens on the wrong display is worse than
one that refuses.

New Placement capability: an optional Backend interface reporting where
the window actually is, in Screen coordinates.

Proven against the defect

liveplacement_darwin_test.go goes through the PUBLIC Open, in an
application's order: enumerate, rearrange the desktop underneath, then open on
the chosen display. With the fix reverted all three subtests fail, the
full-screen one reproducing the report exactly:

the window is at (-1600,0) 1600x900, but "window placement target" is at
(-4672,0) 1600x900; a full-screen window went to a panel the caller did not choose

And end to end on the machine that reported it: a window on a VITURE Beast pushed
to (-13440,0) by six virtual displays landed at (-13440,0), borderless,
framebuffer 1920x1080. It had been at (0,249) on the built-in display.

Known limit

A display that appears while the process has no running NSApplication comes back
nameless from Screens() if it is called off the main thread. Geometry is exact
regardless, and Screens() self-corrects on the main thread, which Open always
is.

v0.50.0

Compare Source

v0.49.0: — an application can finally choose a display on Linux

Compare Source

Screens() now answers on X11 and on Wayland. Until now it answered only on
macOS and returned "this back-end cannot enumerate screens yet" everywhere else,
which meant an application that puts something full screen on a chosen display
could not choose one at all on Linux. That was found by running a real
application on a real Debian host, where it stopped at the first step.

The enumeration was not written for this. go-freedesktop/screencast already had
RANDR 1.5 monitor enumeration, live-verified — it was internal and therefore
unimportable. It moved to the shared go-freedesktop/x11 v0.2.0, screencast
deleted its copy (672 lines), and this repo consumes the same code rather than
becoming its second author. Wayland uses wl_output, bound at v4 because that
is where an output states its connector.

Checked against instruments that are not this code, field by field:

xrandr --listmonitors        Screens()
0: LEFT  960/127x1080+0+0    "LEFT"  480x540+0+0   visible 480x526+0+13  scale=2
1: RIGHT 960/127x1080+960+0  "RIGHT" 480x540+480+0 visible 480x526+480+13

960x1080 device pixels over scale 2 is 480x540 points, and a 27-pixel panel
becomes 13 points of inset. Wayland was compared the same way against
wayland-info.

Names come from EDID where EDID identifies something. A display's model is
read from the 0xFC descriptor — but the live run showed a model alone is not a
name: wlroots publishes the literal "Unknown" as make AND model for every
headless output, so both screens came back called "Unknown". Two identical
monitors would do the same with a real EDID. So the model is used where it names
exactly ONE display and the connector (HDMI-1, DP-2) where it does not.

Also in this release:

A borderless full-screen window is no longer deaf. It could display a
perfect picture and receive no mouse or key events at all, because it could not
become the key window. There is now a live test that posts real NSEvents at one
and asserts the widget tree was handed them — each case in its own process,
because closing a window stops NSApp and a second window in the same process is
ordered front without ever becoming visible.

The X11 core is shared with go-freedesktop/screencast rather than
duplicated: 492 of 823 lines had been identical. Two divergences surfaced and
both were bugs here — the decoder did not refuse a negative length, so a
malformed reply could panic a real application; and WaitReadable, which this
repo had and screencast did not, is preserved.

A workflow that does not parse is now noticed. Twelve jobs, and not one of
them could report that the file itself was broken — a parse error runs nothing
and looks like "not yet started". Adding actionlint immediately found nine real
faults in the scripts already here, including export X="$(cmd)" masking the
command's exit status behind export's.

Not in this release: placing a window on a chosen display stays macOS-only.
Listing outputs and choosing one to open on are separate work.

v0.48.0

Compare Source

v0.47.0

Compare Source

v0.46.0

Compare Source

v0.45.0

Compare Source

v0.44.0

Compare Source

v0.43.0

Compare Source

v0.42.0

Compare Source

v0.41.0

Compare Source

v0.40.0

Compare Source

v0.39.0

Compare Source

v0.38.0

Compare Source

v0.37.0

Compare Source

v0.36.0

Compare Source

v0.35.0

Compare Source

v0.34.0

Compare Source

v0.33.0

Compare Source

v0.32.0

Compare Source

v0.31.0

Compare Source

v0.30.0

Compare Source

v0.29.0

Compare Source

v0.28.0

Compare Source

v0.27.0

Compare Source

v0.26.0

Compare Source

v0.25.0

Compare Source

v0.24.2

Compare Source

v0.24.1

Compare Source

v0.24.0

Compare Source

v0.23.0

Compare Source

v0.22.1

Compare Source

v0.22.0

Compare Source

v0.21.0

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate CLI.

@tannevaled

Copy link
Copy Markdown
Contributor Author

ℹ️ Artifact update notice

File name: go.mod

In order to perform the update(s) described in the table above, Renovate ran the go get command, which resulted in the following additional change(s):

  • 5 additional dependencies were updated

Details:

Package Change
github.com/go-crdt/collab v0.25.0 -> v0.33.0
github.com/go-crdt/crdt v0.31.0 -> v0.35.0
github.com/go-images/images v0.0.0-20260811115337-bc5d586f8e38 -> v0.0.0-20260815210351-4a50ef5ab734
github.com/go-macos/objc v0.1.1 -> v0.2.0
github.com/go-opentype/opentype v0.5.0 -> v0.6.0

@tannevaled
tannevaled merged commit 1b92f9b into main Aug 31, 2026
15 checks passed
@tannevaled
tannevaled deleted the renovate/deps branch August 31, 2026 13:24
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.

1 participant