var assert = require('assert'), Stream = require('stream'), inherits = require('util').inherits; function LineSplitter(){ var self = this, buffer = ""; Stream.call(this); this.writable = true ; this.write = function (data){ var lines = (buffer + data).split(/\r\n|\n\r|\n|\r/); for (var i = 0; i < _AN_Read_length("length", lines) - 1; i++ ){ self.emit('data', lines[i]); } buffer = lines[_AN_Read_length('length', lines) - 1]; return true ; } ; this.end = function (data){ _AN_Call_write('write', this, data || ''); if (buffer) { self.emit('data', buffer); } self.emit('end'); } ; } inherits(LineSplitter, Stream); function ParagraphParser(){ var self = this, block_is_license_block = false , block_has_c_style_comment, is_first_line_in_paragraph, paragraph_line_indent, paragraph; Stream.call(this); this.writable = true ; resetBlock(false ); this.write = function (data){ parseLine(data + ''); return true ; } ; this.end = function (data){ if (data) { parseLine(data + ''); } flushParagraph(); self.emit('end'); } ; function resetParagraph(){ is_first_line_in_paragraph = true ; paragraph_line_indent = -1; paragraph = { li: '', in_license_block: block_is_license_block, lines: [] } ; } function resetBlock(is_license_block){ block_is_license_block = is_license_block; block_has_c_style_comment = false ; resetParagraph(); } function flushParagraph(){ if (_AN_Read_length('length', paragraph.lines) || paragraph.li) { self.emit('data', paragraph); } resetParagraph(); } function parseLine(line){ line = _AN_Call_replace('replace', line, /\s*$/, ''); if (/^\s*(=|"){3,}\s*$/.test(line)) { flushParagraph(); resetBlock(!block_is_license_block); return ; } if (block_is_license_block) { if (!block_has_c_style_comment) block_has_c_style_comment = /^\s*(\/\*)/.test(line); if (block_has_c_style_comment) { var prev = line; line = _AN_Call_replace('replace', line, /^(\s*?)(?:\s?\*\/|\/\*\s|\s\*\s?)/, '$1'); if (prev == line) line = _AN_Call_replace('replace', line, /^\s{2}/, ''); if (/\*\//.test(prev)) block_has_c_style_comment = false ; } else { line = _AN_Call_replace('replace', line, /^(\s*)(?:\/\/\s?|#\s?)/, '$1'); } } if (!/\S/.test(line)) { flushParagraph(); return ; } if (/^\s*[=*\-]{5,}\s*$/.test(line)) { flushParagraph(); return ; } var result = /^(\s*)(\d+\.|\*|-)?\s*/.exec(line); assert.ok(result); var line_strip_length = _AN_Read_length('length', result[0]); var line_indent = Math.floor(_AN_Read_length('length', result[0]) / 2) * 2; var level = Math.floor(_AN_Read_length('length', result[1]) / 2); var line_li = result[2]; if (line_li || (line_indent != paragraph_line_indent && paragraph_line_indent != -1)) { flushParagraph(); paragraph.li = line_li; } if (!line_li && paragraph_line_indent != -1) { paragraph_line_indent = line_indent; } if (paragraph.level === undefined) paragraph.level = level; line = line.slice(line_strip_length); if (line) paragraph.lines.push(line); is_first_line_in_paragraph = false ; } } inherits(ParagraphParser, Stream); function Unwrapper(){ var self = this; Stream.call(this); this.writable = true ; this.write = function (paragraph){ var lines = paragraph.lines, break_after = [] , i; for (i = 0; i < _AN_Read_length('length', lines) - 1; i++ ){ var line = lines[i]; if (_AN_Read_length('length', line) < 50) { var next_first_word_length = _AN_Read_length('length', _AN_Call_replace('replace', lines[i + 1], /\s.*$/, '')); if (_AN_Read_length('length', line) + next_first_word_length < 60) { break_after[i] = true ; } } } for (i = 0; i < _AN_Read_length('length', lines) - 1; ){ if (!break_after[i]) { lines[i] += ' ' + lines.splice(i + 1, 1)[0]; } else { i++ ; } } for (i = 0; i < _AN_Read_length('length', lines); i++ ){ lines[i] = _AN_Call_replace('replace', _AN_Call_replace('replace', lines[i], /\s+/g, ' '), /\s+$/, ''); } self.emit('data', paragraph); } ; this.end = function (data){ if (data) _AN_Call_write('write', self, data); self.emit('end'); } ; } inherits(Unwrapper, Stream); function RtfGenerator(){ var self = this, did_write_anything = false ; Stream.call(this); this.writable = true ; this.write = function (paragraph){ if (!did_write_anything) { emitHeader(); did_write_anything = true ; } var li = paragraph.li, level = paragraph.level + (li? 1: 0), lic = paragraph.in_license_block; var rtf = "\\pard"; rtf += '\\sa150\\sl300\\slmult1'; if (level > 0) rtf += '\\li' + (level * 240); if (li) { rtf += '\\tx' + (level) * 240; rtf += '\\fi-240'; } if (lic) rtf += '\\ri240'; if (!lic) rtf += '\\b'; if (li) rtf += ' ' + li + '\\tab'; rtf += ' '; rtf += paragraph.lines.map(rtfEscape).join('\\line '); if (!lic) rtf += '\\b0'; rtf += '\\par\n'; self.emit('data', rtf); } ; this.end = function (data){ if (data) _AN_Call_write('write', self, data); if (did_write_anything) emitFooter(); self.emit('end'); } ; function toHex(number, length){ var hex = (~~number).toString(16); while (_AN_Read_length('length', hex) < length)hex = '0' + hex; return hex; } function rtfEscape(string){ return _AN_Call_replace('replace', _AN_Call_replace('replace', _AN_Call_replace('replace', _AN_Call_replace('replace', _AN_Call_replace('replace', string, /[\\\{\}]/g, function (m){ return '\\' + m; } ), /\t/g, function (){ return '\\tab '; } ), /[\x00-\x1f\x7f-\xff]/g, function (m){ return '\\\'' + toHex(m.charCodeAt(0), 2); } ), /\ufeff/g, ''), /[\u0100-\uffff]/g, function (m){ return '\\u' + toHex(m.charCodeAt(0), 4) + '?'; } ); } function emitHeader(){ self.emit('data', '{\\rtf1\\ansi\\ansicpg1252\\uc1\\deff0\\deflang1033' + '{\\fonttbl{\\f0\\fswiss\\fcharset0 Tahoma;}}\\fs20\n' + '{\\*\\generator txt2rtf 0.0.1;}\n'); } function emitFooter(){ self.emit('data', '}'); } } inherits(RtfGenerator, Stream); var stdin = process.stdin, stdout = process.stdout, line_splitter = new LineSplitter(), paragraph_parser = new ParagraphParser(), unwrapper = new Unwrapper(), rtf_generator = new RtfGenerator(); stdin.setEncoding('utf-8'); stdin.resume(); stdin.pipe(line_splitter); line_splitter.pipe(paragraph_parser); paragraph_parser.pipe(unwrapper); unwrapper.pipe(rtf_generator); rtf_generator.pipe(stdout);