Added the ability to set a comment in the LRF metadata. Fixed bug in lrf.meta that could result in an invalid LRF file if new metadata is longer than old metadata. Code cleanups in lrf.meta

This commit is contained in:
Kovid Goyal 2007-01-05 21:13:20 +00:00
parent 1a16e40b9e
commit b3faebd2f2
2 changed files with 87 additions and 42 deletions

View File

@ -26,13 +26,12 @@ from math import sin, cos, pi
from libprs500.gui import Error, _Warning from libprs500.gui import Error, _Warning
from libprs500.ptempfile import PersistentTemporaryFile from libprs500.ptempfile import PersistentTemporaryFile
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt, SIGNAL from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.Qt import QApplication, QString, QFont, QAbstractListModel, \ from PyQt4.Qt import QApplication, QString, QFont, QAbstractListModel, \
QVariant, QAbstractTableModel, QTableView, QListView, \ QVariant, QAbstractTableModel, QTableView, QListView, \
QLabel, QAbstractItemView, QPixmap, QIcon, QSize, \ QLabel, QAbstractItemView, QPixmap, QIcon, QSize, \
QSpinBox, QPoint, QPainterPath, QItemDelegate, QPainter, QPen, \ QSpinBox, QPoint, QPainterPath, QItemDelegate, QPainter, \
QColor, QLinearGradient, QBrush, QStyle, \ QPen, QColor, QLinearGradient, QBrush, QStyle, \
QByteArray, QBuffer, QMimeData, \ QByteArray, QBuffer, QMimeData, \
QDrag, QRect QDrag, QRect

View File

@ -26,6 +26,8 @@ to get and set meta information. For example:
import struct import struct
import zlib import zlib
from shutil import copyfileobj
from cStringIO import StringIO
import xml.dom.minidom as dom import xml.dom.minidom as dom
from libprs500.prstypes import field from libprs500.prstypes import field
@ -151,22 +153,54 @@ class xml_field(object):
def __repr__(self): def __repr__(self):
return "XML Field: " + self.tag_name + " in " + self.parent return "XML Field: " + self.tag_name + " in " + self.parent
def insert_into_file(fileobj, data, start, end):
"""
Insert data into fileobj at position C{start}.
This function inserts data into a file, overwriting all data between start
and end. If end == start no data is overwritten. Do not use this function to
append data to a file.
@param fileobj: file like object
@param data: data to be inserted into fileobj
@param start: The position at which to start inserting data
@param end: The position in fileobj of data that must not be overwritten
@return: C{start + len(data) - end}
"""
buffer = StringIO()
fileobj.seek(end)
copyfileobj(fileobj, buffer, -1)
buffer.flush()
buffer.seek(0)
fileobj.seek(start)
fileobj.write(data)
fileobj.flush()
fileobj.truncate()
delta = fileobj.tell() - end # < 0 if len(data) < end-start
copyfileobj(buffer, fileobj, -1)
fileobj.flush()
buffer.close()
return delta
class LRFMetaFile(object): class LRFMetaFile(object):
""" Has properties to read and write all Meta information in a LRF file. """ """ Has properties to read and write all Meta information in a LRF file. """
# The first 8 bytes of all valid LRF files #: The first 6 bytes of all valid LRF files
LRF_HEADER = 'LRF'.encode('utf-16le')+'\0\0' LRF_HEADER = 'LRF'.encode('utf-16le')
lrf_header = fixed_stringfield(length=8, start=0) lrf_header = fixed_stringfield(length=6, start=0x0)
version = field(fmt=WORD, start=8) version = field(fmt=WORD, start=0x8)
xor_key = field(fmt=WORD, start=10) xor_key = field(fmt=WORD, start=0xa)
root_object_id = field(fmt=DWORD, start=12) root_object_id = field(fmt=DWORD, start=0xc)
number_of_objets = field(fmt=QWORD, start=16) number_of_objets = field(fmt=QWORD, start=0x10)
object_index_offset = field(fmt=QWORD, start=24) object_index_offset = field(fmt=QWORD, start=0x18)
binding = field(fmt=BYTE, start=36) binding = field(fmt=BYTE, start=0x24)
dpi = field(fmt=WORD, start=38) dpi = field(fmt=WORD, start=0x26)
width = field(fmt=WORD, start=42) width = field(fmt=WORD, start=0x2a)
height = field(fmt=WORD, start=44) height = field(fmt=WORD, start=0x2c)
color_depth = field(fmt=BYTE, start=46) color_depth = field(fmt=BYTE, start=0x2e)
toc_object_id = field(fmt=DWORD, start=0x44) toc_object_id = field(fmt=DWORD, start=0x44)
toc_object_offset = field(fmt=DWORD, start=0x48) toc_object_offset = field(fmt=DWORD, start=0x48)
compressed_info_size = field(fmt=WORD, start=0x4c) compressed_info_size = field(fmt=WORD, start=0x4c)
@ -266,10 +300,13 @@ class LRFMetaFile(object):
def fset(self, info): def fset(self, info):
self.uncompressed_info_size = len(info) self.uncompressed_info_size = len(info)
stream = zlib.compress(info) stream = zlib.compress(info)
orig_size = self.compressed_info_size
self.compressed_info_size = len(stream) + 4 self.compressed_info_size = len(stream) + 4
self._file.seek(self.info_start) delta = insert_into_file(self._file, stream, self.info_start, \
self._file.write(stream) self.info_start + orig_size - 4)
self._file.flush() self.toc_object_offset += delta
self.object_index_offset += delta
self.update_object_offsets(delta)
return { "fget":fget, "fset":fset, "doc":doc } return { "fget":fget, "fset":fset, "doc":doc }
@ -277,7 +314,7 @@ class LRFMetaFile(object):
def thumbnail_pos(): def thumbnail_pos():
doc = """ The position of the thumbnail in the LRF file """ doc = """ The position of the thumbnail in the LRF file """
def fget(self): def fget(self):
return self.info_start+ self.compressed_info_size-4 return self.info_start + self.compressed_info_size-4
return { "fget":fget, "doc":doc } return { "fget":fget, "doc":doc }
@classmethod @classmethod
@ -313,23 +350,14 @@ class LRFMetaFile(object):
of version <= 800") of version <= 800")
slice = data[0:16] slice = data[0:16]
orig_size = self.thumbnail_size orig_size = self.thumbnail_size
self._file.seek(self.toc_object_offset)
toc = self._file.read(self.object_index_offset - self.toc_object_offset)
self._file.seek(self.object_index_offset)
objects = self._file.read()
self.thumbnail_size = len(data) self.thumbnail_size = len(data)
self._file.seek(self.thumbnail_pos) delta = insert_into_file(self._file, data, self.thumbnail_pos, \
self._file.write(data) self.thumbnail_pos + orig_size)
orig_offset = self.toc_object_offset self.toc_object_offset += delta
self.toc_object_offset = self._file.tell() self.object_index_offset += delta
self._file.write(toc) self.thumbnail_type = self._detect_thumbnail_type(slice)
self.object_index_offset = self._file.tell() self.update_object_offsets(delta)
self._file.write(objects)
self._file.flush()
self._file.truncate() # Incase old thumbnail was bigger than new
self.thumbnail_type = self._detect_thumbnail_type(slice)
# Needed as new thumbnail may have different size than old thumbnail
self.update_object_offsets(self.toc_object_offset - orig_offset)
return { "fget":fget, "fset":fset, "doc":doc } return { "fget":fget, "fset":fset, "doc":doc }
def __init__(self, file): def __init__(self, file):
@ -426,10 +454,14 @@ def main():
import sys, os.path import sys, os.path
from optparse import OptionParser from optparse import OptionParser
from libprs500 import __version__ as VERSION from libprs500 import __version__ as VERSION
parser = OptionParser(usage="usage: %prog [options] mybook.lrf\n\ parser = OptionParser(usage = \
\nWARNING: Based on reverse engineering the LRF format."+\ """%prog [options] mybook.lrf
" Making changes may render your LRF file unreadable. ", \
version=VERSION) Show/edit the metadata in an LRF file.
WARNING: Based on reverse engineering the LRF format.
Making changes may render your LRF file unreadable.
""", version=VERSION)
parser.add_option("-t", "--title", action="store", type="string", \ parser.add_option("-t", "--title", action="store", type="string", \
dest="title", help="Set the book title") dest="title", help="Set the book title")
parser.add_option("-a", "--author", action="store", type="string", \ parser.add_option("-a", "--author", action="store", type="string", \
@ -440,6 +472,9 @@ def main():
parser.add_option("--thumbnail", action="store", type="string", \ parser.add_option("--thumbnail", action="store", type="string", \
dest="thumbnail", help="Path to a graphic that will be"+\ dest="thumbnail", help="Path to a graphic that will be"+\
" set as this files' thumbnail") " set as this files' thumbnail")
parser.add_option("--comment", action="store", type="string", \
dest="comment", help="Path to a txt file containing the "+\
"comment to be stored in the lrf file.")
parser.add_option("--get-thumbnail", action="store_true", \ parser.add_option("--get-thumbnail", action="store_true", \
dest="get_thumbnail", default=False, \ dest="get_thumbnail", default=False, \
help="Extract thumbnail from LRF file") help="Extract thumbnail from LRF file")
@ -459,10 +494,13 @@ def main():
if options.page: if options.page:
lrf.page = options.page lrf.page = options.page
if options.thumbnail: if options.thumbnail:
f = open(options.thumbnail, "r") path = os.path.expanduser(os.path.expandvars(options.thumbnail))
f = open(path, "rb")
lrf.thumbnail = f.read() lrf.thumbnail = f.read()
f.close() f.close()
if options.comment:
path = os.path.expanduser(os.path.expandvars(options.comment))
lrf.free_text = open(path).read()
if options.get_thumbnail: if options.get_thumbnail:
t = lrf.thumbnail t = lrf.thumbnail
td = "None" td = "None"
@ -478,3 +516,11 @@ def main():
print str(f[1]) + ":", lrf.__getattribute__(f[0]) print str(f[1]) + ":", lrf.__getattribute__(f[0])
if options.get_thumbnail: if options.get_thumbnail:
print "Thumbnail:", td print "Thumbnail:", td
def zeroes(num):
temp = [ '\0' for i in range(num) ]
return "".join(temp)
def create_lrf_header():
buffer = StringIO()
buffer.write()