(function attachPyroPng(root) {
  'use strict';

  const signatureLength = 8;
  const signature = [137, 80, 78, 71, 13, 10, 26, 10];
  const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
  const finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const readType = (bytes, offset) => String.fromCharCode(...bytes.slice(offset, offset + 4));

  function crc32(bytes) {
    let crc = 0xffffffff;
    for (const byte of bytes) {
      crc ^= byte;
      for (let bit = 0; bit < 8; bit += 1) crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0);
    }
    return (crc ^ 0xffffffff) >>> 0;
  }

  async function withDpi(blob, dpi) {
    const buffer = await blob.arrayBuffer(); const bytes = new Uint8Array(buffer);
    if (bytes.length < signatureLength || signature.some((value, index) => bytes[index] !== value)) return blob;
    const pixelsPerMeter = Math.round(clamp(finite(dpi, 72), 1, 2400) / .0254);
    const physical = new Uint8Array(21); const view = new DataView(physical.buffer);
    view.setUint32(0, 9); physical.set([112, 72, 89, 115], 4);
    view.setUint32(8, pixelsPerMeter); view.setUint32(12, pixelsPerMeter); physical[16] = 1;
    view.setUint32(17, crc32(physical.subarray(4, 17)));
    const chunks = [bytes.slice(0, signatureLength)]; let offset = signatureLength; let inserted = false;
    while (offset + 12 <= bytes.length) {
      const length = new DataView(bytes.buffer, bytes.byteOffset + offset, 4).getUint32(0); const end = offset + 12 + length;
      if (end > bytes.length) return blob;
      const type = readType(bytes, offset + 4);
      if (type !== 'pHYs') chunks.push(bytes.slice(offset, end));
      if (type === 'IHDR') { chunks.push(physical); inserted = true; }
      offset = end;
    }
    return inserted ? new Blob(chunks, { type: 'image/png' }) : blob;
  }

  function pixelsPerMeter(bytes) {
    let offset = signatureLength;
    while (offset + 12 <= bytes.length) {
      const length = new DataView(bytes.buffer, bytes.byteOffset + offset, 4).getUint32(0); const end = offset + 12 + length;
      if (end > bytes.length) return null;
      if (readType(bytes, offset + 4) === 'pHYs' && length === 9) {
        const view = new DataView(bytes.buffer, bytes.byteOffset + offset + 8, 9);
        return { x: view.getUint32(0), y: view.getUint32(4), unit: view.getUint8(8) };
      }
      offset = end;
    }
    return null;
  }

  const api = Object.freeze({ crc32, withDpi, pixelsPerMeter });
  root.PyroPng = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : window);
