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