# # Copyright (c) 2003, 2006 Art Haas # # This file is part of PythonCAD. # # PythonCAD is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # PythonCAD is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with PythonCAD; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import struct import sys import array _debug = 0 dbg_handle = sys.stdout # # bitstream decoding class # class DWGBitStream(object): __mask_table = [ (0xff, 0, 0x00, 0), # bit offset == 0 (0x7f, 1, 0x80, 7), # bit offset == 1 (0x3f, 2, 0xc0, 6), # bit offset == 2 (0x1f, 3, 0xe0, 5), # bit offset == 3 (0x0f, 4, 0xf0, 4), # bit offset == 4 (0x07, 5, 0xf8, 3), # bit offset == 5 (0x03, 6, 0xfc, 2), # bit offset == 6 (0x01, 7, 0xfe, 1), # bit offset == 7 ] def __init__(self, bits): if not isinstance(bits, array.array): raise TypeError, "Invalid bit array: " + `type(bits)` _tc = bits.typecode if _tc != 'B': raise ValueError, "Unexpected array typecode: %s" % _tc self.__bits = bits self.__offset = 0 def getOffset(self): return self.__offset offset = property(getOffset, None, None, "Offset into bit array") def get_bytes(self, count): if ((count < 0) or (count & 0x07)): raise ValueError, "Invalid count: %d" % count _bits = self.__bits _idx = self.__offset >> 3 _bidx = self.__offset & 0x07 _m1, _lsh, _m2, _rsh = DWGBitStream.__mask_table[_bidx] _bytes = [] _read = 0 while _read < count: if _bidx == 0: _byte = _bits[_idx] _idx = _idx + 1 else: _b1 = (_bits[_idx] & _m1) _idx = _idx + 1 _b2 = (_bits[_idx] & _m2) _byte = (_b1 << _lsh) | (_b2 >> _rsh) _bytes.append(chr(_byte)) self.__offset = self.__offset + 8 _read = _read + 8 return "".join(_bytes) def get_byte(self, count): if ((count < 0) or (count > 8)): raise ValueError, "Invalid count: %d" % count _bits = self.__bits _idx = self.__offset >> 3 _bidx = self.__offset & 0x07 _m = DWGBitStream.__mask_table[_bidx][0] _bav = 8 - _bidx # bits available at the current index _byte = _b1 = 0x0 _rem = 0 if count > _bav: # need bits at next index also _b1 = (_bits[_idx] & _m) _rem = count - _bav else: _byte = ((_bits[_idx] & _m) >> (8 - _bidx - count)) if _rem > 0: _idx = _idx + 1 _byte = (_b1 << _rem) | (_bits[_idx] >> (8 - _rem)) self.__offset = self.__offset + count return _byte def test_bit(self): _bits = self.__bits _idx = self.__offset >> 3 _bidx = self.__offset & 0x07 _mask = 0x1 << (7 - _bidx) _val = (_bits[_idx] & _mask) != 0x0 self.__offset = self.__offset + 1 return _val def set_bit(self, flag): _bits = self.__bits _idx = self.__offset >> 3 _bidx = self.__offset & 0x07 if len(_bits) < _idx: _bits.append(0x0) _val = _bits[_idx] _mask = 0x01 << (7 - _bidx) if flag: _bits[_idx] = _val | _mask else: _bits[_idx] = _val & (0xff ^ _mask) self.__offset = self.__offset + 1 def get_default_double(self, defval): _flags = self.get_byte(2) if (_flags == 0x0): _val = defval else: if (_flags == 0x3): _dstr = self.get_bytes(64) _val = struct.unpack(' 8: # convert string into list of bytes for _chr in _handle: _hlist.append(ord(_chr)) else: _hlist.append(_handle) return (_code, _counter) + tuple(_hlist) def get_modular_short(self, handle): _shorts = [] _short = struct.unpack('>h', handle.read(2))[0] # msb first while (_short & 0x80): # test high bit in lsb byte _shorts.append(_short) _short = struct.unpack('>h', handle.read(2))[0] # msb first _shorts.append(_short) for _i in range(len(_shorts)): # reverse bytes in shorts _short = _shorts[_i] _shorts[_i] = ((_short & 0xff00) >> 8) | ((_short & 0xff) << 8) _slen = len(_shorts) if _slen == 1: _size = _shorts[0] & 0x7fff elif _slen == 2: _tmp = _shorts[0] _shorts[0] = _shorts[1] _shorts[1] = _tmp _size = ((_shorts[0] & 0x7fff) << 15) | (_shorts[1] & 0x7fff) else: raise ValueError, "Unexpected array length: %d" % _slen return _size def read_extended_data(self): _extdata = [] while True: _size = self.get_bit_short() if _size == 0: break _handle = self.get_handle() _eedata = [] while (_size > 0): _cb = self.get_raw_char() _size = _size - 1 if _cb == 0x0: # string _len = self.get_raw_char() _cp = self.get_raw_short() _i = 0 _chars = [] while _i < _len: _chars.append(chr(self.get_raw_char())) _i = _i + 1 _eedata.append("".join(_chars)) _size = _size - _len - 3 elif _cb == 0x1: raise ValueError, "Invalid EEX code byte: 0x1" elif _cb == 0x2: # either '{' or '}' _char = self.get_raw_char() if _char == 0x0: _eedata.append("{") elif _char == 0x1: _eedata.append("}") else: raise ValueError, "Unexpected EEX char: %#02x" % _char _size = _size - 1 elif (_cb == 0x3 or # layer table reference _cb == 0x5): # entity handle reference _chars = [] _i = 0 while _i < 8: _chars.append(self.get_raw_char()) _i = _i + 1 _eedata.append(tuple(_chars)) # this seems odd ... _size = _size - 8 elif _cb == 0x4: # binary data _len = self.get_raw_char() _i = 0 _chars = [] while _i < _len: _chars.append(self.get_raw_char()) _i = _i + 1 _eedata.append(_chars) _size = _size - _len - 1 elif (0xa <= _cb <= 0xd): # three doubles _d1 = self.get_raw_double() _d2 = self.get_raw_double() _d3 = self.get_raw_double() _eedata.append((_d1, _d2, _d3)) _size = _size - 24 elif (0x28 <= _cb <= 0x2a): # one double _d = self.get_raw_double() _eedata.append(_d) _size = _size - 8 elif _cb == 0x46: # short int _short = self.get_raw_short() _eedata.append(_short) _size = _size - 2 elif _cb == 0x47: # long int _long = self.get_raw_long() _eedata.append(_long) _size = _size - 4 else: raise ValueError, "Unexpected code byte: %#02x" % _cb _extdata.append((_handle, _eedata)) return _extdata # # bitstream reading functions for DWG files # # these functions are used in R13/R14/R15 file decoding # # data: an array.array instance of unsigned bytes ("B") # offset: the current bit offset where the value begins # def read_extended_data(data, offset): _bitpos = offset _extdata = [] while True: _bitpos, _size = get_bit_short(data, _bitpos) if _size == 0: break _bitpos, _handle = get_handle(data, _bitpos) _eedata = [] while (_size > 0): _bitpos, _cb = get_raw_char(data, _bitpos) _size = _size - 1 if _cb == 0x0: # string _bitpos, _len = get_raw_char(data, _bitpos) _bitpos, _cp = get_raw_short(data, _bitpos) _chars = [] for _i in range(_len): _bitpos, _char = get_raw_char(data, _bitpos) _chars.append(chr(_char)) _eedata.append("".join(_chars)) _size = _size - _len - 3 elif _cb == 0x1: raise ValueError, "invalid EEX code byte: 0x1" elif _cb == 0x2: # either '{' or '}' _bitpos, _char = get_raw_char(data, _bitpos) if _char == 0x0: _eedata.append("{") elif _char == 0x1: _eedata.append("}") else: raise ValueError, "Unexpected EEX char: %#02x" % _char _size = _size - 1 elif (_cb == 0x3 or # layer table reference _cb == 0x5): # entity handle reference _chars = [] for _i in range(8): _bitpos, _char = get_raw_char(data, _bitpos) _chars.append(_char) _eedata.append(tuple(_chars)) # this seems odd ... _size = _size - 8 elif _cb == 0x4: # binary data _bitpos, _len = get_raw_char(data, _bitpos) _chars = [] for _i in range(_len): _bitpos, _char = get_raw_char(data, _bitpos) _chars.append(_char) _eedata.append(_chars) _size = _size - _len - 1 elif (0xa <= _cb <= 0xd): # three doubles _bitpos, _d1 = get_raw_double(data, _bitpos) _bitpos, _d2 = get_raw_double(data, _bitpos) _bitpos, _d3 = get_raw_double(data, _bitpos) _eedata.append((_d1, _d2, _d3)) _size = _size - 24 elif (0x28 <= _cb <= 0x2a): # one double _bitpos, _d = get_raw_double(data, _bitpos) _eedata.append(_d) _size = _size - 8 elif _cb == 0x46: # short int _bitpos, _short = get_raw_short(data, _bitpos) _eedata.append(_short) _size = _size - 2 elif _cb == 0x47: # long int _bitpos, _long = get_raw_long(data, _bitpos) _eedata.append(_long) _size = _size - 4 else: raise ValueError, "Unexpected code byte: %#02x" % _cb _extdata.append((_handle, _eedata)) return _bitpos, _extdata def get_default_double(data, offset, defval): _flags = get_bits(data, 2, offset) _read = 2 if (_flags == 0x0): _val = defval else: _offset = offset + 2 if (_flags == 0x3): _dstr = get_bits(data, 64, _offset) _val = struct.unpack(' 8: # convert string into list of bytes for _chr in _handle: _hlist.append(ord(_chr)) else: _hlist.append(_handle) return (offset + _read), (_code, _counter) + tuple(_hlist) def get_modular_short(handle): _shorts = [] _short = struct.unpack('>h', handle.read(2))[0] # msb first while (_short & 0x80): # test high bit in lsb byte _shorts.append(_short) _short = struct.unpack('>h', handle.read(2))[0] # msb first _shorts.append(_short) for _i in range(len(_shorts)): # reverse bytes in shorts _short = _shorts[_i] _shorts[_i] = ((_short & 0xff00) >> 8) | ((_short & 0xff) << 8) _slen = len(_shorts) if _slen == 1: _size = _shorts[0] & 0x7fff elif _slen == 2: _tmp = _shorts[0] _shorts[0] = _shorts[1] _shorts[1] = _tmp _size = ((_shorts[0] & 0x7fff) << 15) | (_shorts[1] & 0x7fff) else: raise ValueError, "Unexpected array length: %d" % _slen return _size # # mask1: bit mask to apply to the current byte # lshift: left shift amount of mask results # mask2: bit mask to apply to the next byte # rshift: right shift amount of the mask results # _mask_table = [ (0xff, 0, 0x00, 0), # bit offset == 0 (0x7f, 1, 0x80, 7), # bit offset == 1 (0x3f, 2, 0xc0, 6), # bit offset == 2 (0x1f, 3, 0xe0, 5), # bit offset == 3 (0x0f, 4, 0xf0, 4), # bit offset == 4 (0x07, 5, 0xf8, 3), # bit offset == 5 (0x03, 6, 0xfc, 2), # bit offset == 6 (0x01, 7, 0xfe, 1), # bit offset == 7 ] def get_bits(data, count, offset): # dbg_print("debugging on") dbg_print("passed %d data length with %d count at %d offset" % (len(data), count, offset)) _idx = offset / 8 # index to the byte offset _bitidx = offset % 8 # index to the bit offset within the byte _mask1, _lsh, _mask2, _rsh = _mask_table[_bitidx] _binc = 8 - _bitidx # bits available in current byte _read = 0 _rem = count _byte = 0x0 _bytes = [] while _read < count: if _rem > _binc: # need more bits than this byte can provide dbg_print("_rem > _binc") _b1 = (data[_idx] & _mask1) _read = _read + _binc if not isinstance(_rem, int): dbg_print("rem type: " + str(type(_rem))) dbg_print("rem: " + str(_rem)) if not isinstance(_binc, int): dbg_print("binc type: " + str(type(_binc))) dbg_print("binc: " + str(_binc)) _rem = _rem - _binc else: # this byte can give all the bits needed dbg_print("_rem <= _binc") _byte = _b1 = ((data[_idx] & _mask1) >> (8 - _bitidx - _rem)) _read = _read + _rem _rem = 0 if _read < count: # need bits from next byte dbg_print("_read %d < %d count" % (_read, count)) _idx = _idx + 1 if _rem > _bitidx: # use all bitidx bits - make a complete byte dbg_print("_rem (%d) > (%d) _bitidx" % (_rem, _bitidx)) dbg_print("index %d of %d" % (_idx, len(data))) _b2 = (data[_idx] & _mask2) _byte = (_b1 << _lsh) | (_b2 >> _rsh) _read = _read + _bitidx _rem = _rem - _bitidx else: # use some bitidx to complete bit count request dbg_print("_rem <= _bitidx") _mask = _mask_table[_rem][2] # mask for current byte _b2 = data[_idx] & _mask _byte = (_b1 << _rem) | (_b2 >> (8 - _rem)) _read = _read + _rem _rem = 0 if count > 8: _bytes.append(chr(_byte)) if len(_bytes): return "".join(_bytes) return _byte def test_bit(data, offset): _idx = offset / 8 # index to the byte offset _bitidx = offset % 8 # index to the bit offset within the byte _mask = 0x1 << (7 - _bitidx) _val = False if (data[_idx] & _mask): _val = True return (offset + 1), _val # # debug routines # def set_nodebug(): import dwgutil dwgutil._debug = 0 def set_debug(filename=None): import dwgutil dwgutil._debug = 1 if filename != None: dwgutil.dbg_handle = open(filename, 'w') else: dwgutil.dbg_handle = sys.stdout def dbg_print(*s): if _debug: string = "" for arg in s: string += str(arg) + " " string += "\n" dbg_handle.write(string)