// Pure JS PNG Generator for QR codes // Write a minimal valid PNG with no external dependencies const fs = require('fs'); const zlib = require('zlib'); function crc32(buf) { let crc = 0xFFFFFFFF; const table = new Uint32Array(256); for (let i = 0; i < 256; i++) { let c = i; for (let k = 0; k < 8; k++) c = (c & 1) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1; table[i] = c; } for (let i = 0; i < buf.length; i++) crc = table[(crc ^ buf[i]) & 0xFF] ^ (crc >>> 8); return (crc ^ 0xFFFFFFFF) >>> 0; } function makeChunk(type, data) { const typeB = Buffer.from(type); const lenB = Buffer.alloc(4); lenB.writeUInt32BE(data.length); const crcB = Buffer.alloc(4); crcB.writeUInt32BE(crc32(Buffer.concat([typeB, data]))); return Buffer.concat([lenB, typeB, data, crcB]); } function createPNG(matrix) { const size = matrix.length; const scale = 10; const outSize = size * scale; // Raw image data: filter byte (0) + RGB pixels per row const raw = Buffer.alloc((1 + size * 3) * outSize); for (let y = 0; y < outSize; y++) { const rowOffset = y * (1 + outSize * 3); raw[rowOffset] = 0; // filter type: none const moduleY = Math.floor(y / scale); for (let x = 0; x < outSize; x++) { const moduleX = Math.floor(x / scale); const pixel = matrix[moduleY][moduleX] ? 0 : 255; const offset = rowOffset + 1 + x * 3; raw[offset] = pixel; // R raw[offset + 1] = pixel; // G raw[offset + 2] = pixel; // B } } const compressed = zlib.deflateSync(raw); // PNG signature const signature = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]); // IHDR chunk const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(outSize, 0); // width ihdr.writeUInt32BE(outSize, 4); // height ihdr[8] = 8; // bit depth ihdr[9] = 2; // color type (RGB) ihdr[10] = 0; // compression ihdr[11] = 0; // filter ihdr[12] = 0; // interlace const ihdrChunk = makeChunk('IHDR', ihdr); const idatChunk = makeChunk('IDAT', compressed); const iendChunk = makeChunk('IEND', Buffer.alloc(0)); return Buffer.concat([signature, ihdrChunk, idatChunk, iendChunk]); } // Matrix from our QR generator (21x21 for "TEST") const matrix = [ [1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1], [1,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1], [1,0,1,1,1,0,1,0,1,1,0,1,0,1,0,1,1,1,0,0,1], [1,0,1,1,1,0,1,0,0,1,1,0,1,0,1,0,1,1,1,0,1], [1,0,1,1,1,0,1,0,1,0,1,0,0,1,0,1,1,1,0,0,1], [1,0,0,0,0,0,1,0,1,1,0,1,1,1,1,1,0,0,0,0,1], [1,1,1,1,1,1,1,0,0,0,0,0,1,0,1,1,1,1,1,1,1], [0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0], [1,0,1,0,1,0,1,0,0,0,1,1,0,1,0,1,0,1,1,0,1], [0,1,0,1,0,1,0,1,1,0,0,1,0,1,0,1,1,0,0,1,0], [0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0], [1,1,0,1,0,0,1,0,1,0,1,0,1,0,1,1,0,1,1,1,1], [0,0,0,1,1,1,0,1,0,1,0,1,0,1,1,0,1,1,0,0,0], [1,1,1,0,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0], [1,0,1,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,1,0], [1,0,0,0,0,0,1,0,0,1,1,0,1,0,1,1,1,0,1,0,1], [1,1,1,1,1,1,1,0,1,0,1,0,1,1,0,1,0,1,0,0,1], [0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,1,1,0,1,1,0], [1,1,1,1,1,1,1,0,0,1,0,1,0,0,0,1,1,0,0,0,0], [1,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,0,1,1,1,1], [1,1,1,1,1,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0,0], ]; const png = createPNG(matrix); fs.writeFileSync('/home/node/.openclaw/workspace/assets/qr-test.png', png); console.log('PNG written:', png.length, 'bytes');