FA-79231 / Image orientation metadata / Open access
Print size uses round-half-even · case 01
Exact half tenths such as 2.5 are rounded down on some sizes and up on others.
ROOT CAUSE
round() on a Fraction rounds ties to even instead of half up.
VERIFIED REPAIR
Add one half and floor.
Unsuccessful approach: Flooring truncates every fractional tenth.
Case contract
Input {w, h, tag?, unit?, xres: [num, den], yres?: [num, den]}. ResolutionUnit 2 is inches (25.4 mm), 3 is centimetres (10 mm), a missing unit defaults to 2 and any other unit has no physical size (None). A missing yres equals xres; non-positive rational parts give None. For tags 5..8 the displayed width comes from the stored height at YResolution. Return displayed [width, height] in tenths of a millimetre, each rounded half up from the exact value.
Why this case matters
Camera, phone and scanner images carry an orientation hint separately from the stored pixels; galleries, thumbnailers, editors and upload pipelines must interpret it consistently or photos appear sideways, mirrored or doubly rotated.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
w, h = d['w'], d['h']
tag = d.get('tag', 1)
unit = d.get('unit', 2)
per = {2: Fraction(254, 10), 3: Fraction(10)}.get(unit)
if per is None:
return None
xn, xd = d['xres']
yn, yd = d.get('yres', d['xres'])
if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
return None
xr = Fraction(xn, xd)
yr = Fraction(yn, yd)
if tag in (5, 6, 7, 8):
w, h, xr, yr = h, w, yr, xr
def tenths(px, r):
return round(px * per * 10 / r)
return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 3158, 'h': 1553, 'tag': 6, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [3883, 947]], [{'w': 2042, 'h': 678, 'unit': 3, 'xres': [150, 2]}, [2723, 904]], [{'w': 3779, 'h': 386, 'tag': 2, 'unit': 3, 'xres': [200, 3], 'yres': [600, 1]}, [5669, 64]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 3942, 'h': 988, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1372, 5475]], [{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]], [{'w': 3064, 'h': 189, 'tag': 2, 'unit': 3, 'xres': [7200, 100]}, [4256, 263]]], [[{'w': 2030, 'h': 636, 'tag': 6, 'unit': 2, 'xres': [40, 1], 'yres': [96, 1]}, [1683, 12891]], [{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]], [{'w': 450, 'h': 3183, 'tag': 8, 'unit': 3, 'xres': [200, 3]}, [4775, 675]], [{'w': 1360, 'h': 1067, 'tag': 7, 'xres': [300, 1]}, [903, 1151]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 2636, 'h': 3063, 'unit': 3, 'xres': [200, 3], 'yres': [300, 1]}, [3954, 1021]], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 3609, 'h': 3681, 'tag': 8, 'unit': 3, 'xres': [300, 1], 'yres': [40, 1]}, [9203, 1203]]], [[{'w': 3617, 'h': 3685, 'tag': 1, 'unit': 3, 'xres': [40, 1]}, [9043, 9213]], [{'w': 3823, 'h': 2286, 'tag': 8, 'unit': 3, 'xres': [150, 2], 'yres': [1000, 3]}, [686, 5097]], [{'w': 2508, 'h': 632, 'tag': 2, 'unit': 3, 'xres': [96, 1]}, [2613, 658]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 3633, 'h': 2881, 'xres': [-72, 1]}, None], [{'w': 1570, 'h': 1390, 'tag': 8, 'unit': 1, 'xres': [40, 1], 'yres': [72, 1]}, None], [{'w': 746, 'h': 1989, 'tag': 3, 'unit': 3, 'xres': [7200, 100], 'yres': [40, 1]}, [1036, 4973]]], [[{'w': 2404, 'h': 1499, 'tag': 8, 'unit': 3, 'xres': [200, 3]}, [2249, 3606]], [{'w': 47, 'h': 947, 'tag': 8, 'unit': 2, 'xres': [7200, 100]}, [3341, 166]], [{'w': 3, 'h': 5, 'unit': 3, 'xres': [40, 1], 'yres': [40, 1]}, [8, 13]], [{'w': 2120, 'h': 3278, 'tag': 5, 'xres': [1000, 3], 'yres': [40, 1]}, [20815, 1615]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 776, 'h': 1736, 'tag': 1, 'unit': 3, 'xres': [96, 1]}, [808, 1808]], [{'w': 1975, 'h': 3847, 'tag': 8, 'unit': 2, 'xres': [72, 1], 'yres': [40, 1]}, [24428, 6967]], [{'w': 246, 'h': 1767, 'tag': 1, 'unit': 3, 'xres': [200, 3]}, [369, 2651]]], [[{'w': 3870, 'h': 1382, 'tag': 6, 'unit': 2, 'xres': [40, 1], 'yres': [40, 1]}, [8776, 24575]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]], [{'w': 3, 'h': 5, 'unit': 3, 'xres': [40, 1], 'yres': [40, 1]}, [8, 13]], [{'w': 2499, 'h': 800, 'tag': 6, 'xres': [150, 2]}, [2709, 8463]], [{'w': 2070, 'h': 2469, 'tag': 1, 'unit': 3, 'xres': [0, 1], 'yres': [-300, 1]}, None], [{'w': 901, 'h': 2455, 'tag': 6, 'unit': 3, 'xres': [7200, 100], 'yres': [600, 1]}, [409, 1251]], [{'w': 3766, 'h': 3194, 'tag': 8, 'xres': [150, 2], 'yres': [300, 1]}, [2704, 12754]], [{'w': 2426, 'h': 49, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [40, 1]}, [123, 809]]]]
labels = ["regression: final rounding mode", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("%s %d" % (labels[i % len(labels)], i), solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: final rounding mode 0 | [3882, 947] | [3883, 947] | Failed |
| repair trap 1 | [2723, 904] | [2723, 904] | Passed |
| combined fault 2 | [5668, 64] | [5669, 64] | Failed |
| control 3 | [3150, 1905] | [3150, 1905] | Passed |
| control 4 | None | None | Passed |
| boundary 5 | [1372, 5475] | [1372, 5475] | Passed |
| boundary 6 | [302, 892] | [302, 892] | Passed |
| control 7 | [4256, 262] | [4256, 263] | Failed |
SHA-256 / 9737b7d6d02aa2fabcea59f93984004dee8157c32450698022a9790a6255b855
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
w, h = d['w'], d['h']
tag = d.get('tag', 1)
unit = d.get('unit', 2)
per = {2: Fraction(254, 10), 3: Fraction(10)}.get(unit)
if per is None:
return None
xn, xd = d['xres']
yn, yd = d.get('yres', d['xres'])
if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
return None
xr = Fraction(xn, xd)
yr = Fraction(yn, yd)
if tag in (5, 6, 7, 8):
w, h, xr, yr = h, w, yr, xr
def tenths(px, r):
return math.floor(px * per * 10 / r)
return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 3158, 'h': 1553, 'tag': 6, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [3883, 947]], [{'w': 2042, 'h': 678, 'unit': 3, 'xres': [150, 2]}, [2723, 904]], [{'w': 3779, 'h': 386, 'tag': 2, 'unit': 3, 'xres': [200, 3], 'yres': [600, 1]}, [5669, 64]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 3942, 'h': 988, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1372, 5475]], [{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]], [{'w': 3064, 'h': 189, 'tag': 2, 'unit': 3, 'xres': [7200, 100]}, [4256, 263]]], [[{'w': 2030, 'h': 636, 'tag': 6, 'unit': 2, 'xres': [40, 1], 'yres': [96, 1]}, [1683, 12891]], [{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]], [{'w': 450, 'h': 3183, 'tag': 8, 'unit': 3, 'xres': [200, 3]}, [4775, 675]], [{'w': 1360, 'h': 1067, 'tag': 7, 'xres': [300, 1]}, [903, 1151]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 2636, 'h': 3063, 'unit': 3, 'xres': [200, 3], 'yres': [300, 1]}, [3954, 1021]], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 3609, 'h': 3681, 'tag': 8, 'unit': 3, 'xres': [300, 1], 'yres': [40, 1]}, [9203, 1203]]], [[{'w': 3617, 'h': 3685, 'tag': 1, 'unit': 3, 'xres': [40, 1]}, [9043, 9213]], [{'w': 3823, 'h': 2286, 'tag': 8, 'unit': 3, 'xres': [150, 2], 'yres': [1000, 3]}, [686, 5097]], [{'w': 2508, 'h': 632, 'tag': 2, 'unit': 3, 'xres': [96, 1]}, [2613, 658]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 3633, 'h': 2881, 'xres': [-72, 1]}, None], [{'w': 1570, 'h': 1390, 'tag': 8, 'unit': 1, 'xres': [40, 1], 'yres': [72, 1]}, None], [{'w': 746, 'h': 1989, 'tag': 3, 'unit': 3, 'xres': [7200, 100], 'yres': [40, 1]}, [1036, 4973]]], [[{'w': 2404, 'h': 1499, 'tag': 8, 'unit': 3, 'xres': [200, 3]}, [2249, 3606]], [{'w': 47, 'h': 947, 'tag': 8, 'unit': 2, 'xres': [7200, 100]}, [3341, 166]], [{'w': 3, 'h': 5, 'unit': 3, 'xres': [40, 1], 'yres': [40, 1]}, [8, 13]], [{'w': 2120, 'h': 3278, 'tag': 5, 'xres': [1000, 3], 'yres': [40, 1]}, [20815, 1615]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 776, 'h': 1736, 'tag': 1, 'unit': 3, 'xres': [96, 1]}, [808, 1808]], [{'w': 1975, 'h': 3847, 'tag': 8, 'unit': 2, 'xres': [72, 1], 'yres': [40, 1]}, [24428, 6967]], [{'w': 246, 'h': 1767, 'tag': 1, 'unit': 3, 'xres': [200, 3]}, [369, 2651]]], [[{'w': 3870, 'h': 1382, 'tag': 6, 'unit': 2, 'xres': [40, 1], 'yres': [40, 1]}, [8776, 24575]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]], [{'w': 3, 'h': 5, 'unit': 3, 'xres': [40, 1], 'yres': [40, 1]}, [8, 13]], [{'w': 2499, 'h': 800, 'tag': 6, 'xres': [150, 2]}, [2709, 8463]], [{'w': 2070, 'h': 2469, 'tag': 1, 'unit': 3, 'xres': [0, 1], 'yres': [-300, 1]}, None], [{'w': 901, 'h': 2455, 'tag': 6, 'unit': 3, 'xres': [7200, 100], 'yres': [600, 1]}, [409, 1251]], [{'w': 3766, 'h': 3194, 'tag': 8, 'xres': [150, 2], 'yres': [300, 1]}, [2704, 12754]], [{'w': 2426, 'h': 49, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [40, 1]}, [123, 809]]]]
labels = ["regression: final rounding mode", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("%s %d" % (labels[i % len(labels)], i), solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: final rounding mode 0 | [3882, 947] | [3883, 947] | Failed |
| repair trap 1 | [2722, 904] | [2723, 904] | Failed |
| combined fault 2 | [5668, 64] | [5669, 64] | Failed |
| control 3 | [3150, 1905] | [3150, 1905] | Passed |
| control 4 | None | None | Passed |
| boundary 5 | [1372, 5475] | [1372, 5475] | Passed |
| boundary 6 | [302, 892] | [302, 892] | Passed |
| control 7 | [4255, 262] | [4256, 263] | Failed |
SHA-256 / fc493f83ab5fc634c98b72d50bb565e21b52b5929ac7f614c1baa1c50193b183
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
w, h = d['w'], d['h']
tag = d.get('tag', 1)
unit = d.get('unit', 2)
per = {2: Fraction(254, 10), 3: Fraction(10)}.get(unit)
if per is None:
return None
xn, xd = d['xres']
yn, yd = d.get('yres', d['xres'])
if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
return None
xr = Fraction(xn, xd)
yr = Fraction(yn, yd)
if tag in (5, 6, 7, 8):
w, h, xr, yr = h, w, yr, xr
def tenths(px, r):
return math.floor(px * per * 10 / r + Fraction(1, 2))
return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 3158, 'h': 1553, 'tag': 6, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [3883, 947]], [{'w': 2042, 'h': 678, 'unit': 3, 'xres': [150, 2]}, [2723, 904]], [{'w': 3779, 'h': 386, 'tag': 2, 'unit': 3, 'xres': [200, 3], 'yres': [600, 1]}, [5669, 64]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 3942, 'h': 988, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1372, 5475]], [{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]], [{'w': 3064, 'h': 189, 'tag': 2, 'unit': 3, 'xres': [7200, 100]}, [4256, 263]]], [[{'w': 2030, 'h': 636, 'tag': 6, 'unit': 2, 'xres': [40, 1], 'yres': [96, 1]}, [1683, 12891]], [{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]], [{'w': 450, 'h': 3183, 'tag': 8, 'unit': 3, 'xres': [200, 3]}, [4775, 675]], [{'w': 1360, 'h': 1067, 'tag': 7, 'xres': [300, 1]}, [903, 1151]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 2636, 'h': 3063, 'unit': 3, 'xres': [200, 3], 'yres': [300, 1]}, [3954, 1021]], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 3609, 'h': 3681, 'tag': 8, 'unit': 3, 'xres': [300, 1], 'yres': [40, 1]}, [9203, 1203]]], [[{'w': 3617, 'h': 3685, 'tag': 1, 'unit': 3, 'xres': [40, 1]}, [9043, 9213]], [{'w': 3823, 'h': 2286, 'tag': 8, 'unit': 3, 'xres': [150, 2], 'yres': [1000, 3]}, [686, 5097]], [{'w': 2508, 'h': 632, 'tag': 2, 'unit': 3, 'xres': [96, 1]}, [2613, 658]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 3633, 'h': 2881, 'xres': [-72, 1]}, None], [{'w': 1570, 'h': 1390, 'tag': 8, 'unit': 1, 'xres': [40, 1], 'yres': [72, 1]}, None], [{'w': 746, 'h': 1989, 'tag': 3, 'unit': 3, 'xres': [7200, 100], 'yres': [40, 1]}, [1036, 4973]]], [[{'w': 2404, 'h': 1499, 'tag': 8, 'unit': 3, 'xres': [200, 3]}, [2249, 3606]], [{'w': 47, 'h': 947, 'tag': 8, 'unit': 2, 'xres': [7200, 100]}, [3341, 166]], [{'w': 3, 'h': 5, 'unit': 3, 'xres': [40, 1], 'yres': [40, 1]}, [8, 13]], [{'w': 2120, 'h': 3278, 'tag': 5, 'xres': [1000, 3], 'yres': [40, 1]}, [20815, 1615]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 776, 'h': 1736, 'tag': 1, 'unit': 3, 'xres': [96, 1]}, [808, 1808]], [{'w': 1975, 'h': 3847, 'tag': 8, 'unit': 2, 'xres': [72, 1], 'yres': [40, 1]}, [24428, 6967]], [{'w': 246, 'h': 1767, 'tag': 1, 'unit': 3, 'xres': [200, 3]}, [369, 2651]]], [[{'w': 3870, 'h': 1382, 'tag': 6, 'unit': 2, 'xres': [40, 1], 'yres': [40, 1]}, [8776, 24575]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]], [{'w': 3, 'h': 5, 'unit': 3, 'xres': [40, 1], 'yres': [40, 1]}, [8, 13]], [{'w': 2499, 'h': 800, 'tag': 6, 'xres': [150, 2]}, [2709, 8463]], [{'w': 2070, 'h': 2469, 'tag': 1, 'unit': 3, 'xres': [0, 1], 'yres': [-300, 1]}, None], [{'w': 901, 'h': 2455, 'tag': 6, 'unit': 3, 'xres': [7200, 100], 'yres': [600, 1]}, [409, 1251]], [{'w': 3766, 'h': 3194, 'tag': 8, 'xres': [150, 2], 'yres': [300, 1]}, [2704, 12754]], [{'w': 2426, 'h': 49, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [40, 1]}, [123, 809]]]]
labels = ["regression: final rounding mode", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("%s %d" % (labels[i % len(labels)], i), solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: final rounding mode 0 | [3883, 947] | [3883, 947] | Passed |
| repair trap 1 | [2723, 904] | [2723, 904] | Passed |
| combined fault 2 | [5669, 64] | [5669, 64] | Passed |
| control 3 | [3150, 1905] | [3150, 1905] | Passed |
| control 4 | None | None | Passed |
| boundary 5 | [1372, 5475] | [1372, 5475] | Passed |
| boundary 6 | [302, 892] | [302, 892] | Passed |
| control 7 | [4256, 263] | [4256, 263] | Passed |
SHA-256 / 615763287fd87f0b4af3c2b6d73b1f202c3eb93f67879564e9327182a5374f58
Verification & scope
A deterministic bounded teaching model with a stipulated contract; it makes no claim of conformance to any published specification. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:49:42.527552+00:00.
Case digest / 75970e20f7c45d1d843421e83e76c1181a1f208443229f98f70282b27c649428