IIIF v3: confined size (!w,h) should clamp to region instead of rejecting - #287
IIIF v3: confined size (!w,h) should clamp to region instead of rejecting#287avalyset wants to merge 3 commits into
Conversation
In IIIF Image API v3, a confined "!w,h" request whose box is larger than
the source must return the largest image that fits within the region
("as large as possible but not larger than the extracted region", §4.2),
not be rejected. Previously any v3 request exceeding the region with
upscaling disallowed threw "upscaling should be prefixed with ^",
including confined requests on small sources.
Track confined "!w,h" requests with a dedicated flag and, in that case,
clamp the requested size down to the region rather than throwing; aspect
ratio is preserved downstream in View::getRequestSize(). Forced "w,h" and
single-dimension "w,"/",h" requests that exceed the region are genuine
upscaling and continue to be rejected.
Closes ruven#271
Co-authored-by: Claude <noreply@anthropic.com>
4fe63f5 to
f8a252f
Compare
|
Sonar's gate flags three smells on the new lines, but two of them are the
I did take the one improvement that fit: the clamp now uses Happy to refactor the nesting further if you'd rather have the gate green — I |
On MSVC, <windows.h> defines min/max as macros, so std::min(...) expands to std::(...) and fails to compile (C2589/C2059 at IIIF.cc:566-567). The codebase uses no std::min/std::max elsewhere; replace the two calls with an equivalent ternary clamp, matching the surrounding style. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ar SonarCloud new-code smells The ruven#271 fix had modified the "!w,h" prefix line (adding braces + confined=true), which made Ruven's pre-existing S134/S6178 smells on that line count as new code. Restore that line byte-identical to master and detect the confined flag on a separate single-char check (no substr prefix, so no S6178), so those pre-existing smells revert to the baseline. Also restructure the upscaling branch into an over_region guard with sibling if/else-if (depth 3) plus a clamp_to_region helper, removing the depth-4 nested if (S134). Behaviour and the throw message are identical; equivalence of the confined check verified across empty/"!"/non-"!". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|



Fixes #271.
In IIIF Image API v3, a confined size request
!w,hagainst a region smallerthan the box returns HTTP 400 ("upscaling should be prefixed with ^") instead
of the region at its own size.
Spec
v3 §4.2,
!w,h: the result must be "as large as possible but not larger thanthe extracted region, w or h", aspect preserved. Upscaling uses the separate
^prefix. So!300,300on a 200×200 region should return 200×200, not 400.Cause
The upscale guard in IIIF.cc (around line 551) throws whenever a requested
dimension exceeds the region, with no special case for the confined
!flag.!is stripped at line 505 without recording that the request was confined, solater there's no way to tell it apart from a forced
w,h. maintain_aspectdoesn't help — it's also set for
,handw,, which are genuine upscalerequests that should still throw.
One thing to flag: the downstream aspect fit in View::getRequestSize() has no
no-upscale clamp, so this guard is currently the only thing preventing
upscaling. The clamp therefore has to happen here, in the parse path.
Fix
Record a
confinedflag where!is detected. In the v3 upscale guard, whenthe request is confined and exceeds the region, clamp the requested dimensions
to the region instead of throwing; the downstream aspect fit then produces the
correct result. Forced
w,hand single-dimension,h/w,still throw, and^!w,hstill upscales.The clamp is per-dimension against the region rather than reusing the nearby
max_size clamp, because the region can be non-square while max_size is a scalar
cap.
Verification
Confirmed against the parse logic at IIIF.cc:479-571 (current master), run
through the full chain including View::getRequestSize():
iipsrv has no test suite in-tree, so this was verified with an isolated harness
running the parse block verbatim. A no-upscale test case modelled on the IIIF
validator's size_noup.py would be the natural place for this to live
permanently.