diff --git a/.prettierrc b/.prettierrc index 0aa186e3..9e74d98a 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,3 @@ -{ - "singleQuote": true -} \ No newline at end of file +{ + "singleQuote": true, +} diff --git a/lib/binary.js b/lib/binary.js new file mode 100644 index 00000000..eaca49e3 --- /dev/null +++ b/lib/binary.js @@ -0,0 +1,31 @@ +/* +Binary helpers — Uint8Array-native replacements for Node Buffer operations. +*/ + +export const toBinaryString = (bytes) => { + const chunkSize = 0x8000; + let out = ''; + for (let i = 0; i < bytes.length; i += chunkSize) { + const end = Math.min(i + chunkSize, bytes.length); + out += String.fromCharCode.apply(null, bytes.subarray(i, end)); + } + return out; +}; + +export const readUInt16BE = (bytes, offset = 0) => + ((bytes[offset] << 8) | bytes[offset + 1]) >>> 0; + +export const readUInt16LE = (bytes, offset = 0) => + ((bytes[offset + 1] << 8) | bytes[offset]) >>> 0; + +export const readUInt32BE = (bytes, offset = 0) => + (bytes[offset] * 0x1000000 + + ((bytes[offset + 1] << 16) | + (bytes[offset + 2] << 8) | + bytes[offset + 3])) >>> + 0; + +export const readUInt32LE = (bytes, offset = 0) => + ((bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16)) + + bytes[offset + 3] * 0x1000000) >>> + 0; diff --git a/lib/image/jpeg.js b/lib/image/jpeg.js index 8510df32..7480f632 100644 --- a/lib/image/jpeg.js +++ b/lib/image/jpeg.js @@ -1,6 +1,14 @@ +import { + readUInt16BE, + readUInt16LE, + readUInt32BE, + readUInt32LE, + toBinaryString, +} from '../binary'; + /** * Parse EXIF orientation from JPEG buffer - * @param {Buffer} data - JPEG image data + * @param {Uint8Array} data - JPEG image data * @returns {number|null} Orientation value (1-8) or null if not found */ const parseExifOrientation = (data) => { @@ -13,7 +21,7 @@ const parseExifOrientation = (data) => { while (pos < data.length && data[pos] !== 0xff) pos++; if (pos >= data.length - 4) return null; - const marker = data.readUInt16BE(pos); + const marker = readUInt16BE(data, pos); pos += 2; // SOS marker - image data starts, stop searching @@ -23,28 +31,28 @@ const parseExifOrientation = (data) => { if ((marker >= 0xffd0 && marker <= 0xffd9) || marker === 0xff01) continue; if (pos + 2 > data.length) return null; - const segmentLength = data.readUInt16BE(pos); + const segmentLength = readUInt16BE(data, pos); // APP1 (EXIF) marker if (marker === 0xffe1 && pos + 8 <= data.length) { - const exifHeader = data.subarray(pos + 2, pos + 8).toString('binary'); + const exifHeader = toBinaryString(data.subarray(pos + 2, pos + 8)); if (exifHeader === 'Exif\x00\x00') { const tiffStart = pos + 8; if (tiffStart + 8 > data.length) return null; // Byte order - const byteOrder = data - .subarray(tiffStart, tiffStart + 2) - .toString('ascii'); + const byteOrder = toBinaryString( + data.subarray(tiffStart, tiffStart + 2), + ); const isLittleEndian = byteOrder === 'II'; if (!isLittleEndian && byteOrder !== 'MM') return null; const read16 = isLittleEndian - ? (o) => data.readUInt16LE(o) - : (o) => data.readUInt16BE(o); + ? (o) => readUInt16LE(data, o) + : (o) => readUInt16BE(data, o); const read32 = isLittleEndian - ? (o) => data.readUInt32LE(o) - : (o) => data.readUInt32BE(o); + ? (o) => readUInt32LE(data, o) + : (o) => readUInt32BE(data, o); // Verify TIFF magic number (42) if (read16(tiffStart + 2) !== 42) return null;