Repository navigation
fix(render): stop one-word titles running off the label - #18
Merged
Merged
Conversation
_wrap accepted the first word of every paragraph without measuring it. A title with more than one word got checked from the second word on, but a single word never was, so _fit's search only had height to satisfy and grew the font to max_pt. "INV-SI1" printed as "INV-SI" with the 1 past the edge, and any one-word part name could do the same. Measure the first word like the rest. That exposes a second problem: with room for two lines, splitting "INV-SI1" into "INV-S" and "I1" at a large size also passes the fit. So _fit now searches with whole words first and only allows splitting a word when nothing fits otherwise. measure() stops reporting a natural width narrower than the longest word, which it now would have wrapped mid-word. The existing overflow tests could not catch this: one asserts only the image size, which is fixed, and the other only checks the top row for ink. The new ones fail against the old code, and the whole-word test fails against the first half of the fix alone.
impuls42
added a commit
that referenced
this pull request
Sep 25, 2026
With `qr_base_url = "https://sngn.top/i/"` a phone camera offers to open the label instead of showing `SNGN.TOP/I/PA39` as plain text, which is the whole point of the redirector. But the scheme costs a QR version, and the quiet zone is fixed at the spec's 4 modules inside the presets, so on 12mm tape the code lands at 2px per module. This adds `render.qr_quiet_zone` (default 4, range 1 to 8) and threads it the same way `qr_base_url` goes: config section, worker `_render_cfg`, `RenderConfig`, `PresetContext`, and both preset `QrElement` sites. The render CLI gets `--qr-quiet-zone` to match. Default behaviour doesn't change. Measured on the `part` preset at 12mm, decoding the image rebuilt from the packed device bytes, with the half-resolution round trip standing in for a phone: | base | 4 | 3 | 2 | |---|:--:|:--:|:--:| | `SNGN.TOP/I/` | ✅ | ✅ | ✅ | | `https://sngn.top/i/` | ❌ | ❌ | ✅ | 3 buys nothing. In pixels, the prefixed code's dark area is 50px (2px/module) at 4 and 3, and 75px (3px/module) at 2, which is bigger than the bare code's 63px at 4. The QR box is 81 to 92px here rather than the full 96, because the text takes part of the row. That table is pinned as `test_a_scheme_in_the_base_costs_two_quiet_zone_modules`. The other new tests check that the value actually reaches the worker's `RenderConfig` (a value that stopped at the config section would still print at 4) and that 0 and 9 are rejected at load. This has been running on the D30 here as a local patch since this afternoon. iOS picks up the printed label as a link straight away, and zxing decodes a photo of it with no preprocessing. Unrelated, but found while testing layouts: a one-word title can run off the label. That fix is #18.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A title of
INV-SI1on the stock item preset at 12mm prints asINV-SI. The1is past the right edge._wraphasif not current or font.getlength(trial) <= width, so the first word of each paragraph goes in without being measured. Multi-word titles get checked from the second word on, which is why this hasn't shown up before. A single word never is, so_fit's binary search only has height to satisfy and climbs tomax_pt._fit(120, 60)onINV-SI1returned one line at 45px, which is exactly 16pt, in a 120px box.Measuring the first word fixes that and exposes a second problem.
_wrapsplits an over-long word at the pixel boundary, so in a box tall enough for two lines,INV-S/I1at a big size passes the fit and beats a smaller size that keeps the word whole. For codes that's worse than small text, since a split code reads as two codes._fitnow runs the search with word splitting off first, and only falls back to splitting when nothing fits otherwise.measure()also stops reporting a natural width narrower than the longest word.The existing tests couldn't catch any of this.
test_very_long_text_is_truncated_not_overflowedonly asserts the image size, which is fixed by construction.test_text_stays_inside_the_canvasonly checks the top row for ink. The new tests look at line widths and at the right edge.Mutation check, same four tests:
test_a_word_is_kept_whole_when_a_smaller_size_fitsfailsFound while laying out labels for sengine-cloud/inventree-label-dispatch#10, which puts
INV-SI1-style codes in the title. A part without an IPN gets a title that is justINV-PA12, a single word.