Skip to content

Instantly share code, notes, and snippets.

@batako
Created August 10, 2015 10:59
Show Gist options
  • Select an option

  • Save batako/3317fb608e2851971c55 to your computer and use it in GitHub Desktop.

Select an option

Save batako/3317fb608e2851971c55 to your computer and use it in GitHub Desktop.
var electronicWatermark = (function(){
// 書き込み用バッファ
var _implanted = [];
// チャンク探索
var _process = function(png, type, handler) {
var dataLength;
var chunkType;
var nextChunkPos;
var Signature = String.fromCharCode(137, 80, 78, 71, 13, 10, 26, 10);
var rpos = 0;
// シグネチャの確認
if (String.fromCharCode.apply(null, png.subarray(rpos, rpos += 8)) !== Signature) {
throw new Error('invalid signature');
}
// チャンクの探索
while (rpos < png.length) {
dataLength = (
(png[rpos++] << 24) |
(png[rpos++] << 16) |
(png[rpos++] << 8) |
(png[rpos++] )
) >>> 0;
nextChunkPos = rpos + dataLength + 8;
chunkType = String.fromCharCode.apply(null, png.subarray(rpos, rpos += 4));
if (chunkType === type) {
return handler(png, rpos, dataLength);
}
rpos = nextChunkPos;
}
}
// チャンク作成
var _createNewChunk = function(data) {
var dataLength = data.length;
var chunk = new Uint8Array(4 + 4 + dataLength + 4);
var type = [104, 111, 71, 101];
var crc;
var pos = 0;
var i;
// length
chunk[pos++] = (dataLength >> 24) & 0xff;
chunk[pos++] = (dataLength >> 16) & 0xff;
chunk[pos++] = (dataLength >> 8) & 0xff;
chunk[pos++] = (dataLength ) & 0xff;
// type
chunk[pos++] = type[0];
chunk[pos++] = type[1];
chunk[pos++] = type[2];
chunk[pos++] = type[3];
// data
for (i = 0; i < dataLength; ++i) {
chunk[pos++] = data[i];
}
//crc
crc = Zlib.CRC32.calc(type);
crc = Zlib.CRC32.update(data, crc);
chunk[pos++] = (crc >> 24) & 0xff;
chunk[pos++] = (crc >> 16) & 0xff;
chunk[pos++] = (crc >> 8) & 0xff;
chunk[pos++] = (crc ) & 0xff;
return chunk;
}
// チャンク挿入
var _insertNewChunk = function(implanted, data, png, rpos) {
var chunk = _createNewChunk(data);
var pos = 0;
// IDAT チャンクの前までコピー
implanted.set(png.subarray(0, rpos), pos);
pos += rpos;
// hoGe チャンクをコピー
implanted.set(chunk, pos);
pos += chunk.length;
// IDAT チャンク以降をコピー
implanted.set(png.subarray(rpos), pos);
return implanted;
}
//
var _getImplantedBase64 = function() {
// Uint8Array から bytestring に変換
var implantedString = "";
for (i = 0, il = _implanted.length; i < il; ++i) {
implantedString += String.fromCharCode(_implanted[i]);
}
// Base64 に変換
var implantedBase64 = window.btoa(implantedString);
return implantedBase64;
}
//
var _getURL = function() {
var implantedBase64 = _getImplantedBase64();
var implantedDataURL = 'data:image/png;base64,' + implantedBase64;
return implantedDataURL;
}
//
var _getData = function() {
var extractedData = _process(_implanted, "hoGe", function(png, rpos, length) {
return png.subarray(rpos, rpos += length);
});
var binary = _convertArrayToStr( extractedData );
var result = _convertBinaryToStr( binary );
return result;
}
function _convertArrayToStr(array){
var result = '';
for(var i=0; i < array.length; i++){
result += _zeroFormat(8, (array[i]).toString(2));
}
return result;
}
function _convertStrToArray(str){
var result = [];
for(var i=0; i < str.length; i++){
var num = _zeroFormat(16, (str.charCodeAt(i)).toString(2));
result.push( parseInt( num.slice(0,8), 2) );
result.push( parseInt( num.slice(8,16), 2) );
}
return result;
}
function _convertBinaryToStr(beforeStr){
beforeStr = _toHankakuNum(beforeStr.replace(/\r\n/g,'').replace(/\n/g,'').replace(/\s| /g,''));
if ( !beforeStr.match(/^[01]+$/) || beforeStr.length % 16 != 0){
return;
}
var afterStr = '';
for(var i=0; i < beforeStr.length / 16; i++){
var startNum = i * 16;
var endNum = startNum + 16;
var word = beforeStr.slice(startNum, endNum);
afterStr += String.fromCharCode(parseInt(word,2));
}
return afterStr;
}
function _toHankakuNum(beforeStr){
var han = '0123456789';
var zen = '0123456789';
var afterStr = '';
for (i=0; i<beforeStr.length; i++){
c = beforeStr.charAt(i);
n = zen.indexOf(c,0);
if (n >= 0) c = han.charAt(n);
afterStr += c;
}
return afterStr;
}
function _zeroFormat(max, num){
var tmp=''+num;
while(tmp.length<max){
tmp='0'+tmp;
}
return tmp;
}
var _init = function( b64data, data ) {
if ( !b64data ) {
b64data = '';
}
// 埋め込みデータ
if ( !data ) {
data = '';
} else {
data = _convertStrToArray( data );
}
// Base64 デコード
var decoded = window.atob( b64data );
// Uint8Array に変換
var png = new Uint8Array(
decoded.split('').map(function(char) {
return char.charCodeAt(0);
})
);
_implanted = new Uint8Array(png.length + data.length + 12);
_process(png, 'IDAT', function(png, rpos, length) {
// rpos - 8 = チャンクの開始位置
_insertNewChunk(_implanted, data, png, rpos - 8);
});
}
return {
init: _init,
getBase64: _getImplantedBase64,
getURL: _getURL,
getData: _getData
}
})();
@batako
Copy link
Author

batako commented Aug 10, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment