Fix thumbnail type detection in lrf.meta and initial integration of BBeBook into libprs500

This commit is contained in:
Kovid Goyal 2007-01-05 05:35:27 +00:00
parent ee2c0ca1b0
commit 268405d35c
8 changed files with 261 additions and 29 deletions

View File

@ -14,8 +14,8 @@
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" The GUI to libprs500. Also has ebook library management features. """ """ The GUI to libprs500. Also has ebook library management features. """
__docformat__ = "epytext" __docformat__ = "epytext"
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
APP_TITLE = "libprs500" APP_TITLE = "libprs500"
import pkg_resources, sys, os, re, StringIO, traceback import pkg_resources, sys, os, re, StringIO, traceback
from PyQt4.uic.Compiler import compiler from PyQt4.uic.Compiler import compiler

View File

@ -87,7 +87,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text" > <property name="text" >
<string>For help visit &lt;a href="http://libprs500.kovidgoyal.net">http://libprs500.kovidgoyal.net&lt;/a>&lt;br>&lt;br>&lt;b>libprs500&lt;/b> was created by &lt;b>Kovid Goyal&lt;/b> &amp;copy; 2006&lt;br>%1 %2 %3</string> <string>For help visit &lt;a href="https://libprs500.kovidgoyal.net/wiki/GuiUsage">http://libprs500.kovidgoyal.net&lt;/a>&lt;br>&lt;br>&lt;b>libprs500&lt;/b> was created by &lt;b>Kovid Goyal&lt;/b> &amp;copy; 2006&lt;br>%1 %2 %3</string>
</property> </property>
<property name="textFormat" > <property name="textFormat" >
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>

Binary file not shown.

191
libprs500/lrf/makelrf.py Executable file
View File

@ -0,0 +1,191 @@
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
## This program 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.
##
## This program 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 this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import shutil
import sys
import hashlib
import re
import time
import pkg_resources
import subprocess
from tempfile import mkdtemp
from optparse import OptionParser
import xml.dom.minidom as dom
from libprs500.lrf.meta import LRFException, LRFMetaFile
from libprs500.ptempfile import PersistentTemporaryFile
_bbebook = 'BBeBook-0.2.jar'
def generate_thumbnail(path):
""" Generate a JPEG thumbnail of size ~ 128x128 (aspect ratio preserved)"""
try:
import Image
except ImportError:
raise LRFException("Unable to initialize Python Imaging Library." \
"Thumbnail generation is disabled")
im = Image.open(path)
im.thumbnail((128, 128), Image.ANTIALIAS)
thumb = PersistentTemporaryFile(prefix="makelrf_", suffix=".jpeg")
thumb.close()
im = im.convert()
im.save(thumb.name)
return thumb
def create_xml(cfg):
doc = dom.getDOMImplementation().createDocument(None, None, None)
def add_field(parent, tag, value):
elem = doc.createElement(tag)
elem.appendChild(doc.createTextNode(value))
parent.appendChild(elem)
info = doc.createElement('Info')
info.setAttribute('version', '1.0')
book_info = doc.createElement('BookInfo')
doc_info = doc.createElement('DocInfo')
info.appendChild(book_info)
info.appendChild(doc_info)
add_field(book_info, 'File', cfg['File'])
add_field(doc_info, 'Output', cfg['Output'])
for field in ['Title', 'Author', 'BookID', 'Publisher', 'Label', \
'Category', 'Classification', 'Icon', 'Cover', 'FreeText']:
if cfg.has_key(field):
add_field(book_info, field, cfg[field])
add_field(doc_info, 'Language', 'en')
add_field(doc_info, 'Creator', _bbebook)
add_field(doc_info, 'CreationDate', time.strftime('%Y-%m-%d', time.gmtime()))
doc.appendChild(info)
return doc.toxml()
def makelrf(author=None, title=None, \
thumbnail=None, src=None, odir=".",\
rasterize=True, cover=None):
src = os.path.normpath(os.path.abspath(src))
bbebook = pkg_resources.resource_filename(__name__, _bbebook)
if not os.access(src, os.R_OK):
raise LRFException("Unable to read from file: " + src)
if thumbnail:
thumb = os.path.abspath(options.thumbnail)
if not os.access(thumb, os.R_OK):
raise LRFException("Unable to read from " + thumb)
else:
thumb = pkg_resources.resource_filename(__name__, 'cover.jpg')
if not author:
author = "Unknown"
if not title:
title = os.path.basename(src)
label = os.path.basename(src)
id = hashlib.md5(os.path.basename(label)).hexdigest()
name, ext = os.path.splitext(label)
cwd = os.path.dirname(src)
dirpath = None
try:
if ext == ".rar":
dirpath = mkdtemp('','makelrf')
cwd = dirpath
cmd = " ".join(["unrar", "e", '"'+src+'"'])
proc = subprocess.Popen(cmd, cwd=cwd, shell=True, stderr=subprocess.PIPE)
if proc.wait():
raise LRFException("unrar failed with error:\n\n" + \
proc.stderr.read())
path, msize = None, 0
for root, dirs, files in os.walk(dirpath):
for name in files:
if os.path.splitext(name)[1] == ".html":
size = os.stat(os.path.join(root, name)).st_size
if size > msize:
msize, path = size, os.path.join(root, name)
if not path:
raise LRFException("Could not find .html file in rar archive")
src = path
name = re.sub("\s", "_", name)
name = os.path.abspath(os.path.join(odir, name)) + ".lrf"
cfg = { 'File' : src, 'Output' : name, 'Label' : label, 'BookID' : id, \
'Author' : author, 'Title' : title, 'Publisher' : 'Unknown' \
}
if cover:
cover = os.path.normpath(os.path.abspath(cover))
try:
thumbf = generate_thumbnail(cover)
thumb = thumbf.name
except Exception, e:
print >> sys.stderr, "WARNING: Unable to generate thumbnail:\n", \
str(e)
thumb = cover
cfg['Cover'] = cover
cfg['Icon'] = thumb
config = PersistentTemporaryFile(prefix='makelrf_', suffix='.xml')
config.write(create_xml(cfg))
config.close()
jar = '-jar "' + bbebook + '"'
cmd = " ".join(["java", jar, "-r" if rasterize else "", '"'+config.name+'"'])
proc = subprocess.Popen(cmd, \
cwd=cwd, shell=True, stderr=subprocess.PIPE)
if proc.wait():
raise LRFException("BBeBook failed with error:\n\n" + \
proc.stderr.read())
# Needed as BBeBook-0.2 doesn't handle non GIF thumbnails correctly.
lrf = open(name, "r+b")
LRFMetaFile(lrf).fix_thumbnail_type()
lrf.close()
return name
finally:
if dirpath:
shutil.rmtree(dirpath, True)
def main(cargs=None):
parser = OptionParser(usage=\
"""usage: %prog [options] mybook.[html|pdf|rar]
%prog converts mybook to mybook.lrf
If you specify a rar file you must have the unrar command line client
installed. makelrf assumes the rar file is an archive containing the
html file you want converted."""\
)
parser.add_option("-t", "--title", action="store", type="string", \
dest="title", help="Set the book title")
parser.add_option("-a", "--author", action="store", type="string", \
dest="author", help="Set the author")
parser.add_option('-r', '--rasterize', action='store_true', \
dest="rasterize",
help="Convert pdfs into image files.")
parser.add_option('-c', '--cover', action='store', dest='cover',\
help="Path to a graphic that will be set as the cover. "\
"If it is specified the thumbnail is automatically "\
"generated from it")
parser.add_option("--thumbnail", action="store", type="string", \
dest="thumbnail", \
help="Path to a graphic that will be set as the thumbnail")
if not cargs:
cargs = sys.argv
options, args = parser.parse_args()
if len(args) != 1:
parser.print_help()
sys.exit(1)
src = args[0]
root, ext = os.path.splitext(src)
if ext not in ['.html', '.pdf', '.rar']:
print >> sys.stderr, "Can only convert files ending in .html|.pdf|.rar"
parser.print_help()
sys.exit(1)
name = makelrf(author=options.author, title=options.title, \
thumbnail=options.thumbnail, src=src, cover=options.cover, \
rasterize=options.rasterize)
print "LRF generated:", name

View File

@ -155,7 +155,7 @@ class xml_field(object):
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 8 bytes of all valid LRF files
LRF_HEADER = u'LRF'.encode('utf-16')[2:]+'\0\0' LRF_HEADER = 'LRF'.encode('utf-16le')+'\0\0'
lrf_header = fixed_stringfield(length=8, start=0) lrf_header = fixed_stringfield(length=8, start=0)
version = field(fmt=WORD, start=8) version = field(fmt=WORD, start=8)
@ -281,6 +281,19 @@ class LRFMetaFile(object):
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
def _detect_thumbnail_type(cls, slice):
""" @param slice: The first 16 bytes of the thumbnail """
ttype = 0x14 # GIF
if "PNG" in slice:
ttype = 0x12
if "BM" in slice:
ttype = 0x13
if "JFIF" in slice:
ttype = 0x11
return ttype
@safe_property @safe_property
def thumbnail(): def thumbnail():
doc = \ doc = \
@ -299,6 +312,7 @@ class LRFMetaFile(object):
if self.version <= 800: if self.version <= 800:
raise LRFException("Cannot store thumbnails in LRF files \ raise LRFException("Cannot store thumbnails in LRF files \
of version <= 800") of version <= 800")
slice = data[0:16]
orig_size = self.thumbnail_size orig_size = self.thumbnail_size
self._file.seek(self.toc_object_offset) self._file.seek(self.toc_object_offset)
toc = self._file.read(self.object_index_offset - self.toc_object_offset) toc = self._file.read(self.object_index_offset - self.toc_object_offset)
@ -314,14 +328,7 @@ class LRFMetaFile(object):
self._file.write(objects) self._file.write(objects)
self._file.flush() self._file.flush()
self._file.truncate() # Incase old thumbnail was bigger than new self._file.truncate() # Incase old thumbnail was bigger than new
ttype = 0x14 self.thumbnail_type = self._detect_thumbnail_type(slice)
if data[1:4] == "PNG":
ttype = 0x12
if data[0:2] == "BM":
ttype = 0x13
if data[0:4] == "JIFF":
ttype = 0x11
self.thumbnail_type = ttype
# Needed as new thumbnail may have different size than old thumbnail # Needed as new thumbnail may have different size than old thumbnail
self.update_object_offsets(self.toc_object_offset - orig_offset) self.update_object_offsets(self.toc_object_offset - orig_offset)
return { "fget":fget, "fset":fset, "doc":doc } return { "fget":fget, "fset":fset, "doc":doc }
@ -392,6 +399,11 @@ class LRFMetaFile(object):
self._file.flush() self._file.flush()
def thumbail_extension(self): def thumbail_extension(self):
"""
Return the extension for the thumbnail image type as specified
by L{self.thumbnail_type}. If the LRF file was created by buggy
software, the extension maye be incorrect. See L{self.fix_thumbnail_type}.
"""
ext = "gif" ext = "gif"
ttype = self.thumbnail_type ttype = self.thumbnail_type
if ttype == 0x11: if ttype == 0x11:
@ -401,6 +413,15 @@ class LRFMetaFile(object):
elif ttype == 0x13: elif ttype == 0x13:
ext = "bm" ext = "bm"
return ext return ext
def fix_thumbnail_type(self):
"""
Attempt to guess the thumbnail image format and set
L{self.thumbnail_type} accordingly.
"""
slice = self.thumbnail[0:16]
self.thumbnail_type = self._detect_thumbnail_type(slice)
def main(): def main():
import sys, os.path import sys, os.path

View File

@ -56,4 +56,4 @@ def PersistentTemporaryFile(suffix="", prefix=""):
prefix = "" prefix = ""
fd, name = tempfile.mkstemp(suffix, "libprs500_"+ __version__+"_" + prefix) fd, name = tempfile.mkstemp(suffix, "libprs500_"+ __version__+"_" + prefix)
_file = os.fdopen(fd, "wb") _file = os.fdopen(fd, "wb")
return _TemporaryFileWrapper(_file, name) return _TemporaryFileWrapper(_file, name)

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-4.0.dtd"> <!DOCTYPE Project SYSTEM "Project-4.0.dtd">
<!-- eric4 project file for project prs-500 --> <!-- eric4 project file for project prs-500 -->
<!-- Saved: 2006-12-08, 23:24:29 --> <!-- Saved: 2007-01-03, 23:14:45 -->
<!-- Copyright (C) 2006 Kovid Goyal, kovid@kovidgoyal.net --> <!-- Copyright (C) 2007 Kovid Goyal, kovid@kovidgoyal.net -->
<Project version="4.0"> <Project version="4.0">
<ProgLanguage mixed="0">Python</ProgLanguage> <ProgLanguage mixed="0">Python</ProgLanguage>
<UIType>Qt4</UIType> <UIType>Qt4</UIType>
@ -84,6 +84,15 @@
<Dir>gui</Dir> <Dir>gui</Dir>
<Name>widgets.py</Name> <Name>widgets.py</Name>
</Source> </Source>
<Source>
<Dir>libprs500</Dir>
<Dir>lrf</Dir>
<Name>makelrf.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>ptempfile.py</Name>
</Source>
</Sources> </Sources>
<Forms> <Forms>
<Form> <Form>
@ -230,10 +239,10 @@
</VcsOtherData> </VcsOtherData>
</Vcs> </Vcs>
<FiletypeAssociations> <FiletypeAssociations>
<FiletypeAssociation pattern="*.py" type="SOURCES" />
<FiletypeAssociation pattern="*.ui.h" type="FORMS" /> <FiletypeAssociation pattern="*.ui.h" type="FORMS" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.ui" type="FORMS" /> <FiletypeAssociation pattern="*.ui" type="FORMS" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.py" type="SOURCES" />
<FiletypeAssociation pattern="*.ptl" type="SOURCES" /> <FiletypeAssociation pattern="*.ptl" type="SOURCES" />
</FiletypeAssociations> </FiletypeAssociations>
</Project> </Project>

View File

@ -13,18 +13,25 @@
## with this program; if not, write to the Free Software Foundation, Inc., ## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#!/usr/bin/env python2.5 #!/usr/bin/env python2.5
import sys
import ez_setup import ez_setup
ez_setup.use_setuptools() ez_setup.use_setuptools()
# Try to install the Python imaging library as the package name (PIL) doesn't match the distributionfile name, thus declaring itas a dependency is useless # Try to install the Python imaging library as the package name (PIL) doesn't
#from setuptools.command.easy_install import main as easy_install # match the distribution file name, thus declaring itas a dependency is useless
#try: from setuptools.command.easy_install import main as easy_install
# try: try:
# import Image try:
# except ImportError: import Image
# print "Trying to install thePython Imaging Library" except ImportError:
# easy_install(["-f", "http://www.pythonware.com/products/pil/", "Imaging"]) print "Trying to install the Python Imaging Library"
#except: pass easy_install(["-f", "http://www.pythonware.com/products/pil/", "Imaging"])
except Exception, e:
print >> sys.stderr, e
print >> sys.stderr, \
"WARNING: Could not install the Python Imaging Library.", \
"Some functionality will be unavailable"
import sys import sys
from setuptools import setup, find_packages from setuptools import setup, find_packages
@ -43,11 +50,15 @@ setup(
author='Kovid Goyal', author='Kovid Goyal',
author_email='kovid@kovidgoyal.net', author_email='kovid@kovidgoyal.net',
url = 'http://libprs500.kovidgoyal.net', url = 'http://libprs500.kovidgoyal.net',
package_data = { 'libprs500.gui' : ['*.ui'] }, package_data = { \
'libprs500.gui' : ['*.ui'], \
'libprs500.lrf' : ['*.jar', '*.jpg'] \
},
entry_points = { entry_points = {
'console_scripts': [ \ 'console_scripts': [ \
'prs500 = libprs500.cli.main:main', \ 'prs500 = libprs500.cli.main:main', \
'lrf-meta = libprs500.lrf.meta:main' \ 'lrf-meta = libprs500.lrf.meta:main', \
'makelrf = libprs500.lrf.makelrf:main'\
], ],
'gui_scripts' : [ 'prs500-gui = libprs500.gui.main:main'] 'gui_scripts' : [ 'prs500-gui = libprs500.gui.main:main']
}, },