Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestIntegration_JPEG(t *testing.T) {
{Name: "ApertureValue", Value: "54823/32325"},
{Name: "BrightnessValue", Value: "40874/4739"},
{Name: "ColorSpace", Value: "Uncalibrated"},
{Name: "ComponentsConfiguration", Value: []byte{1, 2, 3, 0}},
{Name: "ComponentsConfiguration", Value: "Y, Cb, Cr, -"},
{Name: "CustomRendered", Value: "Portrait HDR"},
{Name: "DateTimeDigitized", Value: "2019:09:21 14:43:51"},
{Name: "DateTimeOriginal", Value: "2019:09:21 14:43:51"},
Expand Down Expand Up @@ -275,7 +275,7 @@ func TestIntegration_CR2(t *testing.T) {
{Name: "ExifVersion", Value: "0221"},
{Name: "DateTimeOriginal", Value: "2004:11:13 23:02:21"},
{Name: "DateTimeDigitized", Value: "2004:11:13 23:02:21"},
{Name: "ComponentsConfiguration", Value: []byte{1, 2, 3, 0}}, // Y, Cb, Cr, -
{Name: "ComponentsConfiguration", Value: "Y, Cb, Cr, -"}, // Y, Cb, Cr, -
{Name: "ShutterSpeedValue", Value: "434176/65536"},
{Name: "ApertureValue", Value: "65536/65536"},
{Name: "ExposureBiasValue", Value: "0/1"},
Expand Down
62 changes: 62 additions & 0 deletions internal/parser/tiff/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import "fmt"
// decodeEnumValue returns a human-readable string for enum tag values.
// Returns empty string if the tag is not an enum or value is unknown.
func decodeEnumValue(tag uint16, _ string, value any) string {
// Handle ComponentsConfiguration (0x9101) specially - it's a 4-byte array
if tag == 0x9101 {
return decodeComponentsConfiguration(value)
}

// Handle uint16 values (most common for enums)
var v uint16
switch val := value.(type) {
Expand Down Expand Up @@ -417,3 +422,60 @@ func decodeFlashValue(value uint16) string {
}
return result
}

// decodeComponentsConfiguration decodes the ComponentsConfiguration tag (0x9101).
// The value is 4 bytes where each byte represents a component:
// - 0 = does not exist (displayed as "-")
// - 1 = Y (luminance)
// - 2 = Cb (blue chrominance)
// - 3 = Cr (red chrominance)
// - 4 = R (red)
// - 5 = G (green)
// - 6 = B (blue)
func decodeComponentsConfiguration(value any) string {
var bytes []byte

switch v := value.(type) {
case []byte:
bytes = v
case string:
// Handle hex string like "01020300"
if len(v) == 8 {
bytes = make([]byte, 4)
for i := 0; i < 4; i++ {
var b byte
fmt.Sscanf(v[i*2:i*2+2], "%02x", &b)
bytes[i] = b
}
} else {
return ""
}
default:
return ""
}

if len(bytes) < 4 {
return ""
}

componentNames := map[byte]string{
0: "-",
1: "Y",
2: "Cb",
3: "Cr",
4: "R",
5: "G",
6: "B",
}

parts := make([]string, 4)
for i := 0; i < 4; i++ {
if name, ok := componentNames[bytes[i]]; ok {
parts[i] = name
} else {
parts[i] = fmt.Sprintf("%d", bytes[i])
}
}

return parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3]
}
63 changes: 63 additions & 0 deletions internal/parser/tiff/values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,66 @@ func TestEnumMappingsComplete(t *testing.T) {
t.Errorf("ResolutionUnit should have 3 values, got %d", len(tiffEnumValues[0x0128]))
}
}

func TestDecodeComponentsConfiguration(t *testing.T) {
tests := []struct {
name string
value any
expected string
}{
{
name: "YCbCr standard",
value: []byte{1, 2, 3, 0},
expected: "Y, Cb, Cr, -",
},
{
name: "RGB",
value: []byte{4, 5, 6, 0},
expected: "R, G, B, -",
},
{
name: "hex string YCbCr",
value: "01020300",
expected: "Y, Cb, Cr, -",
},
{
name: "hex string RGB",
value: "04050600",
expected: "R, G, B, -",
},
{
name: "empty bytes",
value: []byte{0, 0, 0, 0},
expected: "-, -, -, -",
},
{
name: "too short",
value: []byte{1, 2},
expected: "",
},
{
name: "invalid type",
value: 123,
expected: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := decodeComponentsConfiguration(tt.value)
if result != tt.expected {
t.Errorf("decodeComponentsConfiguration(%v) = %q, want %q",
tt.value, result, tt.expected)
}
})
}
}

func TestDecodeEnumValue_ComponentsConfiguration(t *testing.T) {
// Test that decodeEnumValue handles ComponentsConfiguration (0x9101)
result := decodeEnumValue(0x9101, "ExifIFD", []byte{1, 2, 3, 0})
expected := "Y, Cb, Cr, -"
if result != expected {
t.Errorf("decodeEnumValue for ComponentsConfiguration = %q, want %q", result, expected)
}
}
Loading