Commit Graph
1047 Commits
Author SHA1 Message Date
alehelandClaude Opus 5.5 afdc045e94 craft-audio: review fixes, round 10 (#442)
Tests only; each was confirmed to fail on a mutated encoder.

- Every sample rate code in the frame header table.
- Signals that reach the Rice parameter and LPC shift limits, where a
  value past the limit gives a file decoders refuse.
- ENCODE_EXTENSIONS lists flac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 5617261fdc craft-audio: review fixes, round 9 (#442)
Refuse a linear predictor whose prediction leaves the i32 range. Such a
stream is valid, but symphonia adds the prediction to the residual as an
i32, which panics in a debug build. The round 8 residual limit made this
reachable at 24 bits; without the override, 13 of 240 clipped 32-bit test
streams failed in a debug build and none do now, for 0.09% more bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 82f63f3a4f craft-audio: review fixes, round 8 (#442)
- Code wasted bits (RFC 9639 §9.2.2): low bits that are zero in every
  sample of a subframe were stored anyway, so 16-bit audio exported at 24
  or 32 bits grew by the padding.
- Allow residuals up to the 32-bit limit (§9.2.7.3). The limit was half
  that, so at 32 bits a transient forced its whole block to verbatim.
- README: 32-bit FLAC codes stereo channels independently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 c008e8ee07 craft-audio: review fixes, round 7 (#442)
- Test the high bits of STREAMINFO's total sample count, and that a total
  past 36 bits is left unknown.
- README limits: the usual 5- and 6-channel layouts depend on the mask
  comment too; say what is outside the streamable subset.
- Comments: 32-bit code 0 does not decode everywhere and leaves the
  subset; the oracle test helper is in craft_media_testkit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 430ad15683 craft-audio: review fixes, round 6 (#442)
Tests only, plus docs; the encoder does not change. Each new check was
confirmed to fail on a mutated encoder.

- Tests: frame numbers count up, LPC and Rice partitions and stereo
  decorrelation save the space they should, FileEncoder records a FLAC
  layout, the smallest frame anywhere reaches STREAMINFO, Writer::write
  applies the frame limit, finish leaves the sink at the end, the exact
  655350 Hz cap, the sample size codes.
- README limits: a FLAC layout decoders would not assume depends on its
  mask comment, and ffmpeg and flac ignore a zero mask.
- The encode example's comment named no referent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 82424e59c3 craft-audio: review fixes, round 5 (#442)
- libFLAC and ffmpeg read a FLAC with 5 or 6 channels and no mask comment
  as having side surrounds, so the comment is now written for anything
  else, including the crate's usual back-surround layouts. The round 3
  check treated back surrounds as FLAC's assignment, which lost them.
- Tests: the mask comment rules, opposite-polarity stereo, STREAMINFO
  block and frame sizes.
- A stale comment said only WAV records the layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 8ba7778f8c craft-audio: review fixes, round 4 (#442)
The PICTURE metadata test inserts its block without the last-block flag:
the encoder's VORBIS_COMMENT follows it, and the flagged version was a
broken metadata chain that only decoded by resyncing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 1648f33e6a craft-audio: review fixes, round 3 (#442)
FLAC files record the speaker layout when FLAC's channel assignment
does not already say it, as a WAVEFORMATEXTENSIBLE_CHANNEL_MASK comment.
FLAC's 7-channel order is 6.1 with side surrounds, so a buffer in the
crate's usual 7-channel layout (backs and a back centre) was decoded by
ffmpeg and libFLAC with three channels on the wrong speakers; explicit
layouts such as 5.1 with side surrounds were dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 76df23e86e craft-audio: review fixes, round 2 (#442)
- check_room docs: FLAC holds at most 2^36 - 1 frames.
- The FLAC round trip covers every channel count from 1 to 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 7c88c201a5 craft-audio: review fixes, round 1 (#442)
- FileEncoder/encode for FLAC hand the encoder one 4096-frame block at a
  time: a whole-buffer encode drained the pending samples quadratically
  (480 s of stereo took 19 s, now 5 s) and copied the whole input twice.
- Frame headers name sample rates outside the table (codes 12-14), so
  streams at rates like 11025 or 37800 Hz stay in the streamable subset.
- The decode doctest needs symphonia to read FLAC at this point.
- README: FLAC output is verified against ffmpeg, not the flac tool.
- Tests: keep the AIFF force-RF64 check; exact 2^36 frame boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
alehelandClaude Opus 5.5 550732201c craft-audio: FLAC encoder
FilmCraft's FLAC encoder behind `flac-encode`, kept over SoundCraft's:
linear prediction, stereo decorrelation and the STREAMINFO MD5 give
0.1-37% smaller files on 12 test signals, and the output is bit-exact
through `flac` and ffmpeg. It is 3-5x slower than SoundCraft's, still
about 56x real time.

`lossless-encode` is now `pcm-encode` plus `flac-encode`, and
`EncodeFormat::Flac` writes it through `encode` and `FileEncoder`.
With only one of the two features, the other's formats are refused as
`Unsupported`.

The tests that use FLAC files as decode fixtures (truncation,
corruption, metadata bounds, zero-frame files, container preflight)
come with it, as do the FLAC oracle tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:35:44 +01:00
Aleksander HelgakerandClaude Opus 5.5 38740b43a9 craft-audio: core types, WAV/AIFF read and write, peaks, symphonia (#441)
* craft-audio: core types, WAV/AIFF read and write, peaks, symphonia

The new crate without any codec module: what every later codec plugs
into.

- Shared types: `AudioBuffer`, `AudioInfo`, `ChannelLayout`/`Speaker`,
  `AudioError`, `EncodeOptions` (built with `new` and `with_*`),
  `EncodeFormat`, `BitDepth`, `BwfInfo`, `SampleFormat`.
- WAV/BWF/RF64 and AIFF/AIFF-C reading and writing from SoundCraft's
  `soundcraft-audio-io`, kept over EffectCraft's and FilmCraft's
  smaller writers (only it has BWF, dither, any speaker mask and
  streaming without a known length). `encode` and `FileEncoder` behind
  `pcm-encode`; `lossless-encode` and `encode` turn it on.
- `decode`, `probe`, `tracks` and the streaming, seekable `Decoder`
  (feature `decode-native`). The native path reads WAV, AIFF, and PCM in
  MP4/MOV and Matroska through craft-media's demuxers, packet by packet,
  with edit lists and `CodecDelay`/`DiscardPadding` applied.
  `TrackDecoder` decodes PCM; each codec PR adds its codec to it.
- symphonia 0.6 (MPL-2.0, used unmodified) behind the `symphonia`
  feature, part of `decode`: the fallback for every format with no
  native decoder yet. Its size fields are checked before it reads, and
  its panics are caught where they unwind.
- `peaks`: the min/max/RMS pyramid for waveform drawing.

Workspace: craft-media is a workspace dependency without default
features, so an encode-only craft-audio does not build the MP4
demuxer, and `symphonia-bundle-flac` builds without overflow checks in
dev profiles (its 32-bit LPC restore wraps on valid input).

CI: `audio.yml`, with clippy per feature, rustdoc, WASM, MSRV, and the
tests with ffmpeg oracles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes (CodeRabbit on #441)

- Reading: AIFF that declares frames without an SSND chunk is Malformed; a Matroska PCM track's
  length counts from its first frame; seek targets saturate; an empty FLAC has one track; a
  failed header read is an Io error; deinterleave allocates fallibly; concealed silence is capped
  like a decoded packet, and symphonia's uses the damaged packet's own duration when known.
- Peaks sum squares in f64, so loud float samples give a finite RMS.
- Writing: .aifc maps to AIFF; BWF text is cut at a character boundary; a WAV byte rate past
  32 bits is an error; the FileEncoder docs say nothing may follow the sink's position.
- README: the Limits list says which codecs go through symphonia and which MP4 edit lists apply.
- Tests for each, alias and dither coverage, and a FLAC case in the routing oracle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, second round (CodeRabbit on #441)

- Every header read is checked: `Native::open`, `native::tracks` and the no-symphonia error read
  it with `head_checked`. craft-media demuxer errors that are I/O failures (not a file ending
  early) are returned as `AudioError::Io` instead of sending the file to the other path.
- A Matroska track's presented length runs from its first frame plus `CodecDelay` (unclamped,
  in timestamp ticks with the delay rounded to one) to its end.
- symphonia's concealment allocates fallibly, and a damaged first packet is concealed when the
  demuxer gives its duration. `deinterleave` reserves its channel list fallibly too.
- `TrackDecoder::conceal` documents its cap at the decode limit.
- Tests: an I/O failure past the first 64 KiB, codec delay with the first block at -20, 0 and
  50 ms (checked with `probe`), a seek back after an overshoot, and the loud-sample RMS value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: TrackDecoder::conceal returns a Result

Concealment allocates fallibly and fails with TooLarge when the silence would pass the 2^30-sample
decode limit, instead of returning less than the packet's length. The native path, the README,
the packets example and the doc example pass the error on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, third round (CodeRabbit on #441)

- Concealed silence is held to the decode limit inside pcm::silence, so symphonia's path checks
  frames × channels too, and symphonia's decoded audio is copied into fallibly allocated planes.
- TrackDecoder::reset forgets the last packet's length.
- Without symphonia, a damaged MP4, Matroska or Ogg file (truncated, invalid, no moov) is
  Malformed instead of Unsupported; a feature the demuxer does not read still names the build.
- Tests: an MP4 with no moov in a native-only build, conceal after a reset and past the limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, fourth round (CodeRabbit on #441)

- A file the native demuxers find damaged is Malformed with symphonia too, when symphonia calls
  it unsupported; before, an ftyp-only MP4 was Unsupported in symphonia builds.
- symphonia's decoded blocks are held to the 2^30-sample decode limit before allocating.
- Symph::seek forgets the last packet's length, so a lost packet after a seek is not concealed
  with the length of one before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: review fixes, fifth round (CodeRabbit on #441)

- The damaged-file test covers Matroska as well as MP4, and fails when either branch of
  native::damage is removed.
- native::damage leaves out Ogg: ogg::open reports no damage (it skips damaged pages and calls a
  file with none it can read not Ogg), and symphonia reports damaged Ogg as malformed itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-audio: run the ffmpeg oracle tests on ubuntu-26.04

ubuntu-latest's apt ships ffmpeg 6.1.1, which writes MP2 and E-AC-3 in
Matroska with ends a few milliseconds early, so the routing tests on the
later codec PRs cannot pass on it. ubuntu-26.04 ships ffmpeg 8.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:25:57 +01:00
.CooCoo 893eafc086 craft-update: gate translated button labels with their egui callers (#542) 2026-11-04 09:29:06 +01:00
Dylan M. Taylor fe4e2ff3ef menu: never activate a row from an earlier frame (#574)
Nav kept the rows of the last frame that drew them. When a title click
closed the popup before its rows drew again, Enter or Space ran the
cached command with its old enabled state. Nav::load now clears the rows,
so the keys act only on rows drawn this frame under the current gate.
2026-11-03 23:06:04 -05:00
Dylan M. Taylor 4029f023a6 ui: check floating destinations and integrity-matched font bytes (#567)
- docking: apply_with_permissions refuses a move or open into a floating
  window for a panel that is not floatable (Move, OpenAt, MovePanel and
  group/column/window moves), and a Group placement at a floating anchor,
  which becomes a split of its frame, needs accepts_splits.
- fonts: the served-font loader keys fetched files, in-flight requests,
  failures and pending UI faces by (url, integrity), and installs bytes
  only into faces requiring the integrity they were fetched under
  (Stack::provide_fetched); the public Stack::provide is unchanged.
2026-11-03 23:05:04 -05:00
Dylan M. Taylor d8568d6cfc text: keep hyphenation and line-breaking work linear and bounded (#560)
* text: keep hyphenation and line-breaking work linear and bounded

- hyphen::dict: inflate with a 64 MiB limit (MAX_INFLATED).
- hyphenate_word: walk the sorted break offsets with one iterator.
- patgen::score: index expected breaks once per word.
- patterns: FxHasher's finish rotates the multiplied bits down so packed
  keys sharing low bits spread over buckets; at most MAX_PATTERNS (1M).
- linebreak: Sums precomputes each item's next box or forced break,
  so next_start is a lookup instead of a scan per candidate.

* address review: refuse to serialize hyphenation dictionaries past the inflate limit
2026-11-03 23:04:30 -05:00
Dylan M. Taylor cb82b90580 print: charge skipped whitespace and comments to the lexer's token budget (#547)
* print: charge skipped whitespace and comments to the lexer's token budget

Lexer::skip_ws scanned whitespace and comments without charging them,
so an annotation object behind a long comment was rescanned in full for
every reference to it, outside the per-object and job-wide budgets. The
skipped bytes now count like tokens, and the scan stops at the budget.

* address review: fail objstm load when header lexer exhausts, restore token charge on failed reference lookahead

* address review: charge objstm header lexer tokens to the job budget
2026-11-03 22:58:48 -05:00
FredApps 01315d9c92 Merge pull request #590 from benandez/fix/variant-pack-locale
proof: a variant after the region is looked up without the region first (ca-ES-valencia → ca-valencia)
2026-11-03 23:21:37 +01:00
Martin BallandClaude Opus 5.5 11ed4abc1e proof: the Italian elision test checks dell'anno ranks before dell anno (CodeRabbit)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 22:07:32 +00:00
Martin BallandClaude Opus 5.5 5933160655 proof: a test pins English's elision: false; elision docs say it ties with dropping a typed accent (review)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:46:56 +00:00
Martin BallandClaude Opus 5.5 7a334e5306 proof: a variant after the default script and a region is looked up without the region too (sr-Cyrl-RS-1994 → sr-1994)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:46:07 +00:00
Martin BallandClaude Sonnet 5.5 72e21ec7ca craft-proof: a variant after the region is looked up without the region first (ca-ES-valencia reads the ca-valencia pack) (review: FredApps)
One region_index/without_index helper in lang.rs serves region(), pack_lang::names and packs::locales_for; tests pin the sr-RS-ijekavsk names and the length cut of sr-Cyrl-RS-ijekavsk.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:40:25 +00:00
Martin BallandClaude Sonnet 5.5 18cc6533fa proof: elision docs match the costs; DEFAULT's elision is gated by the dictionary (review: FredApps)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:37:45 +00:00
Martin BallandClaude Opus 5.5 3622f802e7 proof: rank only a true elision first; other apostrophe forms after one-letter fixes
Review of the elision fix: the cheap cost went to every apostrophe form the
dictionary accepts, so English possessives (churchs → church's), contractions
(neer → ne'er) and Catalan enclitics (acusit → acusi't) jumped ahead of the
one-letter fix. A missing apostrophe now ranks first only before a dictionary
word of two letters or more (j'adore, l'home, qu'il, dell'anno) and only when
the language elides (new Tuning::elision, off for English). Any other form costs
COST_MISTYPE + 1, after every one-letter fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:37:45 +00:00
Martin BallandClaude Opus 5.5 d7bbf0a9d4 proof: offer the elided form (J'adore) before the missing space (J adore)
Suggestions tried a missing space but never a missing apostrophe, so French
"Jadore" got "Adore, J adore, Jade" and never "J'adore". Round 4 now also
offers a'b after a short first part (at most 7 letters), ranked before every
split. The dictionary's own check decides whether the elision is right, so
French, Italian and Catalan elision (l'acqua, dell'anno) work and a dictionary
without elided forms is unaffected. Suggestions keep the writer's case and the
apostrophe the dictionary's OCONV writes (J’adore). Costs at most one lookup
per split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCYmv6UJmY2QcuhfXco
2026-11-03 21:37:45 +00:00
Dylan M. Taylor 32b2fd8adf media: cap a Matroska track's content encoding chain (#541)
A track could list any number of ContentEncoding entries, and readers walk
the whole list for every frame they return (frames_readable,
stripped_header). A track with more than 16 entries, far beyond the one or
two a muxer writes, is now treated as having an unsupported compression, so
its frames are returned as stored and the list is never long.
2026-11-03 14:10:50 -05:00
Aleksander HelgakerandClaude Opus 5.5 6865897f14 craft-media: add Ogg demuxer (#440)
* craft-media: Ogg demuxer

FilmCraft's `filmcraft-ogg` as `craft_media::ogg`, behind the opt-in
`ogg` feature: pages (CRC-32 checked, damage skipped by searching for
the next `OggS`), logical streams, packets across pages, and the Ogg
Opus, Vorbis and FLAC mappings. `OpusTiming` gives each Opus packet's
start, the pre-skip and the final page's end trimming.
`Stream::codec_params()` returns `CodecParams` like the other
containers; for FLAC each stream keeps its own header count, and an
unstated count is bounded (it ends at the first audio frame, or at
32 MiB with a warning). `ogg::ByteSource` is `craft_media::ByteSource`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-media: Ogg review fixes: bounded recovery, lost pages, chained links, caps

From the review of the Ogg demuxer (storytold/craft-libs#440):

- The damage scan charges the positions it examines, not a whole 1 MiB read,
  reads 64 KiB at a time, and says when the 64 MiB budget runs out. A file
  with many damaged pages lost its tail without a warning.
- A page whose lacing claims more bytes than the file has left is skipped
  and the scan resumes, instead of ending the parse.
- A gap in a stream's page sequence numbers drops the packet the lost page
  continued; its two ends were joined into one corrupted packet.
- A beginning-of-stream page for a serial already seen starts a new stream,
  so a chained file whose links reuse a serial no longer hands the second
  link's headers to the decoder as audio.
- The packets kept from a file are capped by its size (as Matroska caps
  frames), so zero lacing values cannot make the packet lists many times the
  file. FLAC metadata under an unstated header count must be a block of the
  length its header states, and at most 1024 are kept.
- A packet over 4 GiB is dropped instead of overflowing its size.
- OpusTiming saturates, so hostile granule positions cannot overflow, and a
  clip whose only audio page is also the last is trimmed at the end
  (RFC 7845 §4.5) instead of at the start.
- Speex streams read extra_headers (offset 68) for their header count.
  codec_params gives Theora its Xiph-laced headers and Speex its header,
  rate and channels; the FLAC extradata flags exactly the last block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-media: Ogg damage recovery charges every page it reads and hashes

From CodeRabbit's review of 2308071: a page that fails its CRC was read and
hashed in full (up to about 64 KB) before the parser stepped one byte on,
and the damage scan read 64 KiB per call however near the next page was.
Neither was charged to the recovery budget, so a file packed with fake page
headers could cost gigabytes of reading and hashing.

- A failed page's bytes are charged to the recovery budget; opening stops
  with a warning when it is spent.
- find_capture starts with a 256-byte read and doubles it up to 64 KiB.
- Warnings about damaged pages are capped at 1024, with a closing count of
  the rest.
- Filing a completed packet as header or audio moves into Stream::add_packet,
  keeping open under the cognitive-complexity limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-media: Ogg recovery budget in proportion to the file; stray BOS; header caps

- Damage recovery charges every byte it reads again (a high-water mark), against a budget of
  twice the file's length plus 1 MiB, instead of a fixed 64 MiB that cut off large damaged
  files. A page's body is read without reading its header again, so a page is not charged twice.
- A beginning-of-stream page that carries on the page sequence of a stream with no
  end-of-stream page yet is a stray flag: a warning, and the stream is not split. A link that
  starts again before its end-of-stream page warns.
- Header packet counts a file states (FLAC, Speex) are capped like the unstated FLAC count.
- Reserved FLAC metadata block types (7-126) are metadata blocks.
- Theora codec_params carries the picture size; the extradata table lists Theora and Speex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-media: Ogg header reads checked before they are made; scan span on 32-bit

- A header packet past the size cap is checked by its stated size and not read, and a stream's
  first packet is read once (only its first 80 bytes when it is too large to be a header).
- MAX_HEADER_BYTES is 64 MiB, room for two of the largest FLAC metadata blocks with their block
  headers and the rest of the headers.
- find_capture clamps the scan span in u64 before casting, so a span of 2^32 bytes cannot become
  0 on 32-bit targets.
- The Recovery doc says which reads it charges, and what header packet reads add.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 09:06:14 -05:00
Dylan M. Taylor 55332dd301 font-store: index PostScript names shared by many files in linear time (#538)
Index::assemble searched every earlier position listed under a PostScript
name before adding the next, so an index that gives one name to many files
took time quadratic in their number. Files are visited in order, so a file
that lists a name twice can only be the last position pushed: comparing
with the end is enough.

find_postscript compared each hit's family name with every name collected
so far; it now keeps the lowercased names in a set.
2026-11-02 23:56:48 -05:00
Dylan M. Taylor 6ae033e6c1 Merge pull request #525 from dotCooCoo/feat/doc-pcraft-pixels
craft-doc: PhotoCraft layer pixels and masks behind the pcraft-pixels feature
2026-11-02 23:29:19 -05:00
Dylan M. Taylor 9257c78cef Merge pull request #508 from cdhdt/update-dialog-translations
craft-update: the update dialogs take the app's translator
2026-11-02 22:52:25 -05:00
Dylan M. Taylor 269629ee8c Merge pull request #107 from euisuh/fix/settings-permission-test-portability
test(settings): respect effective filesystem permissions
2026-11-02 22:48:25 -05:00
Dylan M. Taylor 11a6018c60 Merge pull request #524 from dotCooCoo/fix/craft-doc-review-points
craft-doc: read envelopes as the format crates and apps do
2026-11-02 22:48:21 -05:00
Dylan M. Taylor 30bd8483ad Merge pull request #535 from storytold/fix/sec-fontconfig-include-dirs
font-catalog: list each fontconfig include folder once, within a budget
2026-11-02 22:46:46 -05:00
Dylan M. Taylor 071113544b Merge pull request #534 from storytold/fix/sec-docpath-native-prefix
docpath: hold a non-Unicode document's root to the namespace rules
2026-11-02 22:46:42 -05:00
Dylan M. Taylor 2e40c0329f Merge pull request #533 from storytold/fix/sec-cms-inverse-edges
cms: find a sampled inverse curve's flat ends once per pipeline
2026-11-02 22:46:39 -05:00
Dylan M. Taylor c0b58fdb45 Merge pull request #529 from storytold/fix/sec-plate-test-tempdir
plate-test: separate plates in a private random temporary directory
2026-11-02 22:29:15 -05:00
Dylan M. Taylor 299112d789 Merge pull request #528 from storytold/fix/sec-fonts-build-paths
fonts-build: keep manifest font and licence paths inside the checkout
2026-11-02 22:29:12 -05:00
Dylan M. Taylor 0329fe14de Merge pull request #527 from storytold/fix/sec-fonts-build-cmap
fonts-build: read each shared cmap subtable once
2026-11-02 22:29:09 -05:00
Dylan M. Taylor 7eac928440 Merge pull request #468 from carkrueger/fix/cascade-on-real-monitors
window: cascade new windows on the real monitor
2026-11-02 22:26:48 -05:00
Dylan M. Taylor 9a20fdbe1d Merge pull request #267 from fluorine/fix/fonts-build-ui-paths
Fix native path assertion in font embedding test
2026-11-02 22:24:39 -05:00
Dylan M. Taylor 90f9f9aa18 Merge pull request #476 from dotCooCoo/fix/keymap-iso-key-shift-symbol
keymap: test that Shift on the ISO key typing a symbol is dropped
2026-11-02 22:24:35 -05:00
Dylan M. Taylor 2bd8f60e15 Merge pull request #367 from lehni/fix/native-bar-title
docking: a native floating window's bar shows no title, as its tab names the panel
2026-11-02 22:16:24 -05:00
Dylan M. Taylor 9d4f20aa03 Merge pull request #522 from vangel76/perf/bitreader-expgolomb-517
bitstream: decode short Exp-Golomb codes with one peek, write ue(v) in one call (#517)
2026-11-02 22:07:16 -05:00
dotCooCoo 3cdeca266a craft-doc: bound shared tile conversion and reject trailing frames 2026-11-02 19:07:05 -08:00
Dylan M. Taylor 1508ae6d63 Merge pull request #521 from vangel76/perf/rbsp-unescape-518
bitstream: copy runs in unescape_rbsp instead of pushing byte by byte (#518)
2026-11-02 22:06:14 -05:00
Dylan M. Taylor 8e717b26da Merge pull request #519 from vangel76/perf/bitreader-peek-516
bitstream: one 8-byte load in BitReader::peek, bit reads 2.5x faster (#516)
2026-11-02 22:05:14 -05:00
Dylan M. Taylor 0535bc87f1 Merge pull request #146 from midasmatterhorn/fix/r4-cms-cube-domain
cms: write_cube keeps a non-default input domain
2026-11-02 22:01:55 -05:00
Dylan M. Taylor 40bb05042f Merge pull request #474 from dotCooCoo/fix/krilla-gray-black
krilla: a gray stop in a CMYK gradient becomes its exact black (patch 17)
2026-11-02 21:57:50 -05:00
Dylan M. Taylor 9be9a63782 Merge pull request #426 from mockydevs/fix/path-as-rect-open-polyline
pdf reader: a five-point open polyline is not a rectangle
2026-11-02 21:53:06 -05:00
Dylan M. Taylor 3c5e0201e0 docpath: fold . and .. in a non-Unicode document's folder with the platform's path rules
A non-Unicode document path holding '..' (/a/../\xff/d.doc) was refused,
because the native join kept the '..' while the lexical result folded it.
join_native now folds the folder's components as normalize does, so such a
document resolves as a Unicode one would, and a '..' that leaves a Unix name
holding a backslash is still caught by the comparison.
2026-11-02 21:52:51 -05:00