Skip to content

Commit

Permalink
Add code to create a png from a string
Browse files Browse the repository at this point in the history
  • Loading branch information
Amadeus Beckmann committed Oct 23, 2018
1 parent fb35693 commit f7a6eb5
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 1 deletion.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018
Copyright (c) 2018 lamadeus <[email protected]> (https://github.com/lamadeus)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
17 changes: 17 additions & 0 deletions es/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Buffer } from 'buffer';
import { createPng } from './png';
import { prependLength } from './utils';


/**
* Encode the given data to png.
*
* @param {string} data
* @returns {Promise<Buffer>}
*/
export function encode(data) {
const bytes = Buffer.from(data, 'utf8');
const pixelData = prependLength(bytes);

return createPng(pixelData);
}
128 changes: 128 additions & 0 deletions es/png.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Buffer } from 'buffer';
import { crc32 } from 'crc';
import zlib from 'zlib';


/**
* PNG header.
*
* @type {Buffer}
*/
const PNG_HEADER = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);

/**
* Max width of the png.
*
* @type {number}
*/
const MAX_WIDTH = 1024;

/**
* Create a chunk of the given type with the given data.
*
* @param type
* @param data
* @returns {Buffer}
*/
function createChunk(type, data) {
const length = typeof data != 'undefined' ? data.length : 0;
const chunk = Buffer.alloc(4 + 4 + length + 4);

chunk.writeUInt32BE(length, 0);
chunk.fill(type, 4, 8, 'utf8');
if (typeof data != 'undefined') {
chunk.fill(data, 8, chunk.length - 4);
}
chunk.writeUInt32BE(crc32(chunk.slice(4, -4)), chunk.length - 4);

return chunk;
}

/**
* Create the IHDR chunk.
*
* @param width
* @param height
* @returns {Buffer}
*/
function createIHDRChunk(width, height) {
const data = Buffer.alloc(13);

// Width
data.writeUInt32BE(width);
// Height
data.writeUInt32BE(height, 4);
// Bit depth
data.writeUInt8(8, 8);
// RGBA mode
data.writeUInt8(6, 9);
// No compression
data.writeUInt8(0, 10);
// No filter
data.writeUInt8(0, 11);
// No interlacing
data.writeUInt8(0, 12);

return createChunk('IHDR', data);
}

/**
* Create the IDAT chunk.
*
* @param data
* @returns {Buffer}
*/
function createIDATChunk(data) {
return createChunk('IDAT', data);
}

/**
* Create the IEND chunk.
*
* @returns {Buffer}
*/
function createIENDChunk() {
return createChunk('IEND');
}

/**
* Create a png from the given pixel data.
*
* @param pixelData
* @returns {Promise<Buffer>}
*/
export function createPng(pixelData) {
const length = pixelData.length + 4 - (pixelData.length % 4);
const pixels = Math.ceil(length / 4);
const width = Math.min(pixels, MAX_WIDTH);
const height = Math.ceil(pixels / MAX_WIDTH);
const bytesPerRow = width * 4;
const buffer = Buffer.alloc((bytesPerRow + 1) * height);

// Write pixel data to buffer
for (let y = 0; y < height; y += 1) {
const offset = y * bytesPerRow;
const rowData = pixelData.slice(offset, offset + bytesPerRow);
const rowStartX = offset + y + 1;
const rowEndX = rowStartX + bytesPerRow;

buffer.writeUInt8(0, offset + y);
buffer.fill(rowData, rowStartX, rowEndX);
}

return new Promise((resolve, reject) => {
zlib.deflate(buffer, (error, deflated) => {
if (error) {
reject(error);
return;
}

resolve(Buffer.concat([
PNG_HEADER,
createIHDRChunk(width, height),
createIDATChunk(deflated),
createIENDChunk(),
]));
});
});
}
16 changes: 16 additions & 0 deletions es/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Buffer } from 'buffer';


/**
* Prepend length to the data.
*
* @param data
* @returns {Buffer}
*/
export function prependLength(data) {
const lengthAsBytes = Buffer.alloc(4);

lengthAsBytes.writeUInt32BE(data.length);

return Buffer.concat([lengthAsBytes, data]);
}
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './es';
30 changes: 30 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "data-to-png",
"version": "1.0.0",
"description": "A Javascript utility that lets you encode any data to png.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/lamadeus/data-to-png.git"
},
"keywords": [
"javascript",
"encode",
"decode",
"png"
],
"author": "lamadeus <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/lamadeus/data-to-png/issues"
},
"homepage": "https://github.com/lamadeus/data-to-png#readme",
"dependencies": {
"buffer": "^5.2.1",
"crc": "^3.8.0",
"zlib": "^1.0.5"
}
}
28 changes: 28 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


base64-js@^1.0.2:
version "1.3.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"

buffer@^5.1.0, buffer@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"

crc@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
dependencies:
buffer "^5.1.0"

ieee754@^1.1.4:
version "1.1.12"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"

zlib@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0"

0 comments on commit f7a6eb5

Please sign in to comment.