///|
/// Read the canvas dimensions from a Photoshop PSD / PSB header without decoding
/// any layer or pixel data. Width and height are stored big-endian near the top
/// of the file.
///|
pub fn psd_dimensions(bytes : Bytes) -> (Int32, Int32)? {
if bytes.length() < 22 {
return None
}
if !bytes.has_prefix(b"8BPS") {
return None
}
// Version must be 1 (PSD); PSB uses version 2 and larger values, which this
// reader intentionally does not accept because PSB uses 64-bit dimensions.
if bytes[4].to_int() != 0 || bytes[5].to_int() != 1 {
return None
}
let channels = read_big_endian_u16(bytes, 12)
if channels < 1 || channels > 56 {
return None
}
let height = read_big_endian_u32(bytes, 14)
let width = read_big_endian_u32(bytes, 18)
if width <= 0L ||
height <= 0L ||
width >= 2_147_483_648L ||
height >= 2_147_483_648L {
return None
}
Some((Int64::to_int(width), Int64::to_int(height)))
}
///|
async fn read_psd_dimensions(path : String, size : Int64) -> (Int32, Int32)? {
if size < 22L {
return None
}
let file = @fs.open(path, mode=@fs.ReadOnly)
defer file.close()
let header = file.read_exactly(22)
psd_dimensions(header)
}
///|
test "reads PSD width and height from the header" {
// 8BPS, version 1, 6 reserved bytes, 4 channels, height 40, width 64
let psd : Bytes = b"8BPS\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x28\x00\x00\x00\x40"
assert_true(psd_dimensions(psd) == Some((64, 40)))
}
///|
test "rejects malformed PSD headers" {
assert_true(psd_dimensions(b"not a psd") is None)
// wrong version (2 = PSB)
let psb : Bytes = b"8BPS\x00\x02\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x28\x00\x00\x00\x40"
assert_true(psd_dimensions(psb) is None)
// zero channels
let no_channels : Bytes = b"8BPS\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x40"
assert_true(psd_dimensions(no_channels) is None)
let short : Bytes = b"8BPS\x00\x01"
assert_true(psd_dimensions(short) is None)
}