require 'stringio' require 'strscan' require 'rtf' module Mapi # # = Introduction # # The +RTF+ module contains a few helper functions for dealing with rtf # in mapi messages: +rtfdecompr+, and rtf2html. # # Both were ported from their original C versions for simplicity's sake. # module RTF RTF_PREBUF = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \ "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript " \ "\\fdecor MS Sans SerifSymbolArialTimes New RomanCourier" \ "{\\colortbl\\red0\\green0\\blue0\n\r\\par " \ "\\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx" # Decompresses compressed rtf +data+, as found in the mapi property # +PR_RTF_COMPRESSED+. Code converted from my C version, which in turn # I wrote from a Java source, in JTNEF I believe. # # C version was modified to use circular buffer for back references, # instead of the optimization of the Java version to index directly into # output buffer. This was in preparation to support streaming in a # read/write neutral fashion. def rtfdecompr data io = StringIO.new data buf = RTF_PREBUF + "\x00" * (4096 - RTF_PREBUF.length) wp = RTF_PREBUF.length rtf = '' # get header fields (as defined in RTFLIB.H) compr_size, uncompr_size, magic, crc32 = io.read(16).unpack 'V*' #warn "compressed-RTF data size mismatch" unless io.size == data.compr_size + 4 # process the data case magic when 0x414c454d # "MELA" magic number that identifies the stream as a uncompressed stream rtf = io.read uncompr_size when 0x75465a4c # "LZFu" magic number that identifies the stream as a compressed stream flag_count = -1 flags = nil while rtf.length < uncompr_size and !io.eof? # each flag byte flags 8 literals/references, 1 per bit flags = ((flag_count += 1) % 8 == 0) ? io.getc : flags >> 1 if 1 == (flags & 1) # each flag bit is 1 for reference, 0 for literal rp, l = io.getc, io.getc # offset is a 12 byte number. 2^12 is 4096, so thats fine rp = (rp << 4) | (l >> 4) # the offset relative to block start l = (l & 0xf) + 2 # the number of bytes to copy l.times do rtf << buf[wp] = buf[rp] wp = (wp + 1) % 4096 rp = (rp + 1) % 4096 end else rtf << buf[wp] = io.getc wp = (wp + 1) % 4096 end end else # unknown magic number raise "Unknown compression type (magic number 0x%08x)" % magic end # not sure if its due to a bug in the above code. doesn't seem to be # in my tests, but sometimes there's a trailing null. we chomp it here, # which actually makes the resultant rtf smaller than its advertised # size (+uncompr_size+). rtf.chomp! 0.chr rtf end # Note, this is a conversion of the original C code. Not great - needs tests and # some refactoring, and an attempt to correct some inaccuracies. Hacky but works. # # Returns +nil+ if it doesn't look like an rtf encapsulated rtf. # # Some cases that the original didn't deal with have been patched up, eg from # this chunk, where there are tags outside of the htmlrtf ignore block. # # "{\\*\\htmltag116
}\\htmlrtf \\line \\htmlrtf0 \\line {\\*\\htmltag84