UNPKG

ansi-graphics

Version:
676 lines (610 loc) 22.7 kB
var util = require('util'), fs = require('fs'), spawn = require('child_process').spawn, events = require('events'), Canvas = require('canvas'), Image = Canvas.Image, GIFEncoder = require('gifencoder'), defs = require('./defs.js'); var copyObject = function(obj) { var ret = {}; for(var property in obj) if(Array.isArray(obj[property])) ret[property] = obj[property]; else if(typeof obj[property] == "object") ret[property] = copyObject(obj[property]); else if(typeof obj[property] != "undefined") ret[property] = obj[property]; return ret; } // Very shallow comparison var compareObjects = function(obj1, obj2) { var ret = true; for(var property in obj1) { if(obj1[property] === obj2[property]) continue; ret = false; break; } return ret; } var ANSI = function() { var self = this; events.EventEmitter.call(this); this.data = []; var width = 0; var height = 0; this.__defineGetter__( "width", function() { return width + 1; } ); this.__defineGetter__( "height", function() { return height + 1; } ); this.__defineGetter__( "pixelWidth", function() { return (9 * (width + 1)); } ); this.__defineGetter__( "pixelHeight", function() { return (16 * (height + 1)); } ); this.fromString = function(ansiString) { var plain = ""; var cursor = { 'x' : 0, 'y' : 0 }; var cursorStore = { 'x' : 0, 'y' : 0 }; var graphics = { 'bright' : false, 'blink' : false, 'foreground' : 37, 'background' : 40 }; while(ansiString.length > 0) { var regex = /^\u001b\[(\d*;?)*[a-zA-Z]/; var result = regex.exec(ansiString); if(result === null) { var chr = { 'cursor' : copyObject(cursor), 'graphics' : copyObject(graphics), 'chr' : ansiString.substr(0, 1) }; switch(chr.chr.charCodeAt(0)) { case 13: cursor.x = 0; break; case 10: cursor.y++; break; default: cursor.x++; if(cursor.x == 80) { cursor.x = 0; cursor.y++; } this.data.push(chr); break; } ansiString = ansiString.substr(1); } else { var ansiSequence = ansiString.substr(0, result[0].length).replace(/^\u001b\[/, ""); var cmd = ansiSequence.substr(ansiSequence.length - 1); var opts = ansiSequence.substr(0, ansiSequence.length - 1).split(";"); opts.forEach( function(e, i, a) { a[i] = parseInt(e); } ); ansiString = ansiString.substr(result[0].length); switch(cmd) { case 'A': if(isNaN(opts[0])) opts[0] = 1; cursor.y = Math.max(cursor.y - opts[0], 0); break; case 'B': if(isNaN(opts[0])) opts[0] = 1; cursor.y = cursor.y + opts[0]; break; case 'C': if(isNaN(opts[0])) opts[0] = 1; cursor.x = Math.min(cursor.x + opts[0], 79); break; case 'D': if(isNaN(opts[0])) opts[0] = 1; cursor.x = Math.max(cursor.x - opts[0], 0); break; case 'f': cursor.y = (isNaN(opts[0])) ? 1 : opts[0]; cursor.x = (opts.length < 2) ? 1 : opts[1]; break; case 'H': cursor.y = (isNaN(opts[0])) ? 1 : opts[0]; cursor.x = (opts.length < 2) ? 1 : opts[1]; break; case 'm': for(var o in opts) { var i = parseInt(opts[o]); if(opts[o] == 0) { graphics.foreground = 37; graphics.background = 40; graphics.bright = false; graphics.blink = false; } else if(opts[o] == 1) { graphics.bright = true; } else if(opts[o] == 5) { graphics.blink = true; } else if(opts[o] >= 30 && opts[o] <= 37) { graphics.foreground = opts[o]; } else if(opts[o] >= 40 && opts[o] <= 47) { graphics.background = opts[o]; } } break; case 's': cursorStore = copyObject(cursor); break; case 'u': cursor = copyObject(cursorStore); break; case 'J': if(opts.length == 1 && opts[0] == 2) { /* for(var d in this.data) { var o = copyObject(this.data[d]); o.chr = " "; this.data.push(o); cursor.y = 0; cursor.x = 0; } */ for(var y = 0; y < 24; y++) { for(var x = 0; x < 80; x++) { this.data.push( { 'cursor' : { 'x' : x, 'y' : y }, 'graphics' : { 'bright' : false, 'blink' : false, 'foreground' : 37, 'background' : 40 }, 'chr' : " " } ); } } } break; case 'K': for(var d in this.data) { if(this.data[d].cursor.y != cursor.y || this.data[d].cursor.x < cursor.x) continue; var o = copyObject(this.data[d]); o.chr = " "; this.data.push(o); } break; default: // Unknown or unimplemented command break; } } width = Math.max(cursor.x, width); height = Math.max(cursor.y, height); } } this.fromFile = function(fileName) { var contents = fs.readFileSync(fileName, { 'encoding' : 'binary' }); this.fromString(contents); } this.__defineGetter__( "matrix", function() { var ret = {}; for(var d = 0; d < self.data.length; d++) { if(typeof ret[self.data[d].cursor.y] == "undefined") ret[self.data[d].cursor.y] = {}; ret[self.data[d].cursor.y][self.data[d].cursor.x] = { 'graphics' : copyObject(self.data[d].graphics), 'chr' : self.data[d].chr }; } for(var y = 0; y <= height; y++) { if(typeof ret[y] == "undefined") ret[y] = {}; for(var x = 0; x <= width; x++) { if(typeof ret[y][x] != "undefined") continue; ret[y][x] = { 'graphics' : { 'bright' : false, 'blink' : false, 'foreground' : 37, 'background' : 40 }, 'chr' : " " } } } return ret; } ); this.__defineGetter__( "plainText", function() { var lines = []; var matrix = self.matrix; for(var y in matrix) { var line = ""; for(var x in matrix[y]) line += matrix[y][x].chr; lines.push(line); } return lines.join("\r\n") + "\r\n"; } ); this.__defineGetter__( "HTML", function() { var graphics = { 'bright' : false, 'blink' : false, 'foreground' : 37, 'background' : 40 }; var graphicsToSpan = function(graphics) { var span = util.format( '<span style="background-color: %s; color: %s;">', defs.Attributes[graphics.background].htmlLow, (graphics.bright) ? defs.Attributes[graphics.foreground].htmlHigh : defs.Attributes[graphics.foreground].htmlLow ); return span; } var lines = [ '<pre style="font-family: Courier New, Courier, monospace; font-style: normal; font-weight: normal; letter-spacing: -1px; line-height: 1;">', graphicsToSpan(graphics) ]; var matrix = self.matrix; for(var y in matrix) { var line = ""; for(var x in matrix[y]) { if(!compareObjects(matrix[y][x].graphics, graphics)) { line += "</span>" + graphicsToSpan(matrix[y][x].graphics); graphics = copyObject(matrix[y][x].graphics); } line += (typeof defs.ASCIItoHTML[matrix[y][x].chr.charCodeAt(0)] == "undefined") ? ((matrix[y][x].chr == " ") ? "&nbsp;" : matrix[y][x].chr) : "&#" + defs.ASCIItoHTML[matrix[y][x].chr.charCodeAt(0)].entityNumber + ";"; } lines.push(line); } lines.push("</span>"); lines.push("</pre>\n"); return lines.join("\n"); } ); this.__defineGetter__( "binary", function() { var matrix = self.matrix; var bin = []; var width = 0; for(var y in matrix) { for(var x in matrix[y]) { width = Math.max(x, width); bin.push(matrix[y][x].chr.charCodeAt(0)); bin.push( defs.Attributes[matrix[y][x].graphics.foreground].attribute|defs.Attributes[matrix[y][x].graphics.background].attribute|((matrix[y][x].graphics.bright)?defs.Attributes[1].attribute:0)|((matrix[y][x].graphics.blink)?defs.Attributes[5].attribute:0) ); } } return new Buffer(bin); } ); this.toGIF = function(options) { if(typeof options != "object") options = {}; if(typeof options.scale != "number") options.scale = 1; var encoder = new GIFEncoder( Math.ceil(this.pixelWidth * options.scale), Math.ceil(this.pixelHeight * options.scale) ); var rs = encoder.createReadStream(); encoder.start(); encoder.setRepeat( (typeof options.loop != "boolean" || !options.loop) ? -1 : 0 ); encoder.setDelay( (typeof options.delay != "number") ? 40 : Math.round(options.delay) ); encoder.setQuality( (typeof options.quality != "number") ? 20 : Math.min(20, options.quality) ); var frames = (typeof options.charactersPerFrame != "number") ? 20 : Math.round(options.charactersPerFrame); var canvas = new ansiCanvas( this.pixelWidth, this.pixelHeight, options.scale ); for(var d = 0; d < self.data.length; d++) { canvas.putCharacter( self.data[d].cursor.x, self.data[d].cursor.y, self.data[d].chr.charCodeAt(0), defs.Attributes[self.data[d].graphics.foreground].attribute|((self.data[d].graphics.bright)?defs.Attributes[1].attribute:0), (defs.Attributes[self.data[d].graphics.background].attribute>>4) ); if(d % frames == 0) encoder.addFrame(canvas.context); } encoder.setDelay(5000); // Dwell on the last frame for a while. Make configurable? encoder.addFrame(canvas.context); encoder.finish(); return rs.read(); } this.toPNG = function(options) { if(typeof options != "object") options = {}; var matrix = self.matrix; var canvas = new ansiCanvas( this.pixelWidth, this.pixelHeight, (typeof options.scale == "number") ? options.scale : 1, (typeof options.quality == "number" && options.quality >= 0 && options.quality <= 4) ? options.quality : 4 ); for(var y in matrix) { for(var x in matrix[y]) { canvas.putCharacter( x, y, matrix[y][x].chr.charCodeAt(0), defs.Attributes[matrix[y][x].graphics.foreground].attribute|((matrix[y][x].graphics.bright)?defs.Attributes[1].attribute:0), (defs.Attributes[matrix[y][x].graphics.background].attribute>>4) ); } } return canvas.canvas.toBuffer(); } this.toVideo = function(options, callback) { if(arguments.length == 1 && typeof options != "function") this.emit("error", "ANSI.toMovie: Invalid callback"); else if(arguments.length == 1) var callback = options; if(arguments.length > 1 && (typeof options != "object" || typeof callback != "function")) this.emit("error", "ANSI.toMovie: Invalid arguments"); var movie = new Buffer(0); var canvas = new ansiCanvas( this.pixelWidth, this.pixelHeight, (typeof options.scale == "number") ? options.scale : 1 ); var child = spawn( 'ffmpeg', [ '-y', '-loglevel', 'quiet', '-f', 'image2pipe', '-c:v', 'png', '-r', (typeof options.frameRate != "number") ? 30 : options.frameRate, '-i', 'pipe:0', '-f', (typeof options.format == "undefined") ? "webm" : options.format, '-filter:v', 'setpts=' + ((typeof options.speed != "number") ? 1 : options.speed) + '*PTS', 'pipe:1' ] ); child.on( "close", function() { callback(movie); } ); child.stderr.on( "data", function(data) { console.log(data.toString()); } ); child.stdout.on( "data", function(data) { movie = Buffer.concat([movie, data]); } ); for(var d = 0; d < self.data.length; d++) { canvas.putCharacter( self.data[d].cursor.x, self.data[d].cursor.y, self.data[d].chr.charCodeAt(0), defs.Attributes[self.data[d].graphics.foreground].attribute|((self.data[d].graphics.bright)?defs.Attributes[1].attribute:0), (defs.Attributes[self.data[d].graphics.background].attribute>>4) ); if(d % ((typeof options.charactersPerFrame != "number") ? 20 : options.charactersPerFrame) == 0) child.stdin.write(canvas.canvas.toBuffer()); } child.stdin.write(canvas.canvas.toBuffer()); child.stdin.end(); } } util.inherits(ANSI, events.EventEmitter); // Lazily ported and modified from my old HTML5 ANSI editor // Could be simplified and folded into ANSI.toGIF() at some point var ansiCanvas = function(width, height, scale, quality) { var foregroundCanvas, foregroundContext, backgroundCanvas, backgroundContext, mergeCanvas, mergeContext; var properties = { 'width' : width, 'height' : height, 'scale' : (typeof scale != "number") ? 1 : scale, 'quality' : (typeof quality != "number" || quality < 1 || quality > 5) ? 4 : quality - 1, 'characters' : [], 'spriteSheet' : new Image(), 'spriteWidth' : 9, 'spriteHeight' : 16, 'colors' : [ "#000000", "#0000A8", "#00A800", "#00A8A8", "#A80000", "#A800A8", "#A85400", "#A8A8A8", "#545454", "#5454FC", "#54FC54", "#54FCFC", "#FC5454", "#FC54FC", "#FCFC54", "#FFFFFF" ], 'qualityMap' : [ 'bilinear', 'nearest', 'better', 'good', 'fast' ] }; var merge = function() { mergeContext.drawImage( backgroundCanvas, 0, 0, properties.width, properties.height, 0, 0, Math.ceil(properties.width * properties.scale), Math.ceil(properties.height * properties.scale) ); mergeContext.drawImage( foregroundCanvas, 0, 0, properties.width, properties.height, 0, 0, Math.ceil(properties.width * properties.scale), Math.ceil(properties.height * properties.scale) ); } this.__defineGetter__( "canvas", function() { merge(); return mergeCanvas; } ); this.__defineGetter__( "context", function() { merge(); return mergeContext; } ); var initSpriteSheet = function() { properties.spriteSheet.src = "data:image/png;base64,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"; for( var y = 0; y <= properties.spriteSheet.height; y = y + properties.spriteHeight ) { for( var x = 0; x < properties.spriteSheet.width; x = x + properties.spriteWidth ) { properties.characters.push( { 'x' : x, 'y' : y } ); } } } var initCanvas = function() { backgroundCanvas = new Canvas(properties.width, properties.height); backgroundContext = backgroundCanvas.getContext('2d'); backgroundContext.fillStyle = properties.colors[0]; backgroundContext.fillRect(0, 0, properties.width, properties.height); foregroundCanvas = new Canvas(properties.width, properties.height); foregroundContext = foregroundCanvas.getContext('2d'); mergeCanvas = new Canvas(Math.ceil(properties.width * properties.scale), Math.ceil(properties.height * properties.scale)); mergeContext = mergeCanvas.getContext('2d'); mergeContext.patternQuality = properties.qualityMap[properties.quality]; mergeContext.filter = properties.qualityMap[properties.quality]; } this.putCharacter = function(x, y, character, foregroundColor, backgroundColor) { x = Math.floor( (x * properties.spriteWidth) / properties.spriteWidth ) * properties.spriteWidth; y = Math.floor( (y * properties.spriteHeight) / properties.spriteHeight ) * properties.spriteHeight; foregroundContext.clearRect( x, y, properties.spriteWidth, properties.spriteHeight ); foregroundContext.drawImage( properties.spriteSheet, properties.characters[character].x, properties.characters[character].y, properties.spriteWidth, properties.spriteHeight, x, y, properties.spriteWidth, properties.spriteHeight ); foregroundContext.globalCompositeOperation = 'source-atop'; foregroundContext.fillStyle = properties.colors[foregroundColor]; foregroundContext.fillRect( x, y, properties.spriteWidth, properties.spriteHeight ); foregroundContext.globalCompositeOperation = 'source-over'; backgroundContext.fillStyle = properties.colors[backgroundColor]; backgroundContext.fillRect( x, y, properties.spriteWidth, properties.spriteHeight ); } initSpriteSheet(); initCanvas(); } module.exports = ANSI;