// SPDX-License-Identifier: LGPL-3.0-or-later // Read only the bounded Exif item, never search compressed image bytes for TIFF // signatures. HEIF transforms take precedence to avoid applying rotation twice. export function itemLocations(bytes,start,end){ const bad=()=>Error('Unsupported HEIC orientation metadata. Convert it to JPG first.'),view=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength);let position=start; function number(size){if(size>8||position+size>end)throw bad();let n=0;for(let i=0;i2)throw bad();number(3);const sizes=number(1),other=number(1),offsetSize=sizes>>4,lengthSize=sizes&15,baseSize=other>>4,indexSize=version?other&15:0; const count=number(version<2?2:4);if(count>10000)throw bad();const items=new Map(); for(let i=0;i64)throw bad(); for(let j=0;jsum+e.length,0); if(total<12||total>1024*1024)throw Error('Unsupported HEIC orientation metadata. Convert it to JPG first.'); const payload=new Uint8Array(total);let position=0; for(const extent of location.extents){ const start=location.base+extent.offset+(location.method===1?idat?.start||0:0),end=start+extent.length; if(!Number.isSafeInteger(start)||!Number.isSafeInteger(end)||start<0||end>bytes.length||location.method===1&&(!idat||startidat.end))throw Error('Unsupported HEIC orientation metadata. Convert it to JPG first.'); payload.set(bytes.subarray(start,end),position);position+=extent.length; } const value=tiffOrientation(payload);if(orientation!==1&&value!==orientation)throw Error('Unsupported HEIC orientation metadata. Convert it to JPG first.');orientation=value; } return orientation; } export function tiffOrientation(payload){ const invalid=()=>Error('Unsupported HEIC orientation metadata. Convert it to JPG first.'); if(payload.length<12)throw invalid(); const view=new DataView(payload.buffer,payload.byteOffset,payload.byteLength),start=4+view.getUint32(0); if(start+8>payload.length)throw invalid(); const marker=view.getUint16(start),little=marker===0x4949;if(!little&&marker!==0x4d4d||view.getUint16(start+2,little)!==42)throw invalid(); const offset=start+view.getUint32(start+4,little);if(offset+2>payload.length)throw invalid(); const count=view.getUint16(offset,little);if(count>4096||offset+2+12*count>payload.length)throw invalid(); for(let i=0;i8)throw invalid();return value; } return 1; } export function orientRgba(data,width,height,orientation){ if(orientation===1)return {data,width,height}; if(!Number.isInteger(orientation)||orientation<1||orientation>8||data.length!==width*height*4)throw Error('Invalid image orientation.'); const swapped=orientation>=5,w=swapped?height:width,h=swapped?width:height,output=new Uint8ClampedArray(data.length); for(let y=0;y