bwip-js
Version:
JavaScript barcode generator supporting over 100 types and standards.
206 lines (181 loc) • 6.58 kB
JavaScript
// drawing-zlibpng.js
//
var PNGTYPE_PALETTE = 3;
var PNGTYPE_TRUEALPHA = 6;
var PNG_TEXT = "Software\0bwip-js.metafloor.com";
var PNG_CRC = (function() {
var precalc = [];
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
if (c & 1) {
c = 0xedb88320 ^ (c >>> 1);
} else {
c = c >>> 1;
}
}
precalc[i] = c;
}
return precalc;
})();
// This has been moved to the nodejs-only section of exports.js due to
// react-native polyfills.
//var PNG_ZLIB = require('zlib');
// `maybe` maybe the callback, pre v4.0.
function DrawingZlibPng(callback, maybe) {
// Pre setops() backward compatibility.
if (maybe && typeof maybe == 'function') {
callback = maybe;
}
var image_buffer, image_width, image_height;
// Provide our specializations for the builtin drawing
var drawing = DrawingBuiltin();
drawing.image = image;
drawing.end = end;
// Reflect setopts() into the super
var opts;
var _setopts = drawing.setopts;
drawing.setopts = function (options) {
opts = options;
_setopts && _setopts.call(drawing, options);
};
return drawing;
// Called by DrawingBuiltin.init() to get the RGBA image data for rendering.
function image(width, height) {
// PNG RGBA buffers are prefixed with a one-byte filter type
image_buffer = Buffer.alloc ? Buffer.alloc(width * height * 4 + height)
: new Buffer(width * height * 4 + height);
image_width = width;
image_height = height;
// Set background
if (/^[0-9a-fA-F]{6}$/.test(''+opts.backgroundcolor)) {
var rgb = opts.backgroundcolor;
fillRGB(parseInt(rgb.substr(0,2), 16),
parseInt(rgb.substr(2,2), 16),
parseInt(rgb.substr(4,2), 16));
}
// The return value is designed to accommodate both canvas pure-RGBA buffers
// and PNG's row-filter prefixed RGBA buffers.
return { buffer:image_buffer, ispng:true };
}
function fillRGB(r, g, b) {
var color = ((r << 24) | (g << 16) | (b << 8) | 0xff) >>> 0;
// This is made complex by the filter byte that prefixes each row...
var len = image_width * 4 + 1;
var row = Buffer.alloc ? Buffer.alloc(len) : new Buffer(len);
for (var i = 1; i < len; i += 4) {
row.writeUInt32BE(color, i);
}
image_buffer.fill(row);
}
function end() {
if (!callback) {
return new Promise(makePNG);
} else {
makePNG(function(png) { callback(null, png); }, function(err) { callback(err); });
}
}
function makePNG(resolve, reject) {
// DEFLATE the image data
var bufs = [];
var buflen = 0;
var deflator = PNG_ZLIB.createDeflate({
chunkSize: 32 * 1024,
level : PNG_ZLIB.Z_DEFAULT_COMPRESSION,
strategy: PNG_ZLIB.Z_DEFAULT_STRATEGY });
deflator.on('error', reject);
deflator.on('data', function(data) { bufs.push(data); buflen += data.length; });
deflator.on('end', returnPNG);
deflator.end(image_buffer);
function returnPNG() {
var length = 8 + 12 + 13 + // PNG Header + IHDR chunk
12 + PNG_TEXT.length + // tEXt
12 + buflen + // IDAT
12; // IEND
if (opts.dpi) {
length += 12 + 9; // pHYs
}
// Emulate a byte-stream
var png = Buffer.alloc(length);
var pngoff = 0; // running offset into the png buffer
write('\x89PNG\x0d\x0a\x1a\x0a'); // PNG file header
writeIHDR();
writeTEXT();
if (opts.dpi) {
writePHYS();
}
writeIDAT();
writeIEND();
// Success
resolve(png);
function writeIHDR() {
write32(13); // chunk length
var crcoff = pngoff;
write('IHDR');
write32(image_width);
write32(image_height);
write8(8); // bit depth
write8(PNGTYPE_TRUEALPHA);
write8(0); // compression default
write8(0); // filter default
write8(0); // no interlace
writeCRC(crcoff);
}
function writeTEXT() {
write32(PNG_TEXT.length); // chunk length
var crcoff = pngoff;
write('tEXt');
write(PNG_TEXT);
writeCRC(crcoff);
}
function writePHYS() {
write32(9);
var crcoff = pngoff;
var pxm = ((opts.dpi || 72) / 0.0254)|0;
write('pHYs');
write32(pxm); // x-axis
write32(pxm); // y-axis
write8(1); // px/m (the only usable option)
writeCRC(crcoff);
}
function writeIDAT() {
write32(buflen); // chunk length
var crcoff = pngoff;
write('IDAT');
for (var i = 0; i < bufs.length; i++) {
bufs[i].copy(png, pngoff);
pngoff += bufs[i].length;
}
writeCRC(crcoff);
}
function writeIEND() {
write32(0); // chunk length;
var crcoff = pngoff;
write('IEND');
writeCRC(crcoff);
}
function write(s) {
png.write(s, pngoff, 'binary');
pngoff += s.length;
}
function write32(v) {
png.writeUInt32BE(v, pngoff);
pngoff += 4;
}
function write16(v) {
png.writeUInt16BE(v, pngoff);
pngoff += 2;
}
function write8(v) {
png[pngoff++] = v;
}
function writeCRC(off) {
var crc = -1;
while (off < pngoff) {
crc = PNG_CRC[(crc ^ png[off++]) & 0xff] ^ (crc >>> 8);
}
write32((crc ^ -1) >>> 0);
}
}
}
}