sync to pluginize

This commit is contained in:
John Schember 2009-05-05 06:51:18 -04:00
commit a2a1ea70f3
37 changed files with 2599 additions and 326 deletions

View File

@ -3,7 +3,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys, os, re, logging, time, subprocess, mimetypes, \ import sys, os, re, logging, time, subprocess, mimetypes, \
__builtin__, warnings __builtin__, warnings, multiprocessing
__builtin__.__dict__['dynamic_property'] = lambda(func): func(None) __builtin__.__dict__['dynamic_property'] = lambda(func): func(None)
from htmlentitydefs import name2codepoint from htmlentitydefs import name2codepoint
from math import floor from math import floor
@ -265,40 +265,7 @@ class StreamReadWrapper(object):
def detect_ncpus(): def detect_ncpus():
"""Detects the number of effective CPUs in the system""" """Detects the number of effective CPUs in the system"""
try: return multiprocessing.cpu_count()
from PyQt4.QtCore import QThread
ans = QThread.idealThreadCount()
if ans > 0:
return ans
except:
pass
#for Linux, Unix and MacOS
if hasattr(os, "sysconf"):
if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
#Linux and Unix
ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
if isinstance(ncpus, int) and ncpus > 0:
return ncpus
else:
#MacOS X
try:
return int(subprocess.Popen(('sysctl', '-n', 'hw.cpu'), stdout=subprocess.PIPE).stdout.read())
except IOError: # Occassionally the system call gets interrupted
try:
return int(subprocess.Popen(('sysctl', '-n', 'hw.cpu'), stdout=subprocess.PIPE).stdout.read())
except IOError:
return 1
except ValueError: # On some systems the sysctl call fails
return 1
#for Windows
if os.environ.has_key("NUMBER_OF_PROCESSORS"):
ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
if ncpus > 0:
return ncpus
#return the default value
return 1
def launch(path_or_url): def launch(path_or_url):
if os.path.exists(path_or_url): if os.path.exists(path_or_url):

View File

@ -64,6 +64,10 @@ class OptionRecommendation(object):
self.validate_parameters() self.validate_parameters()
@property
def help(self):
return self.option.help
def clone(self): def clone(self):
return OptionRecommendation(recommended_value=self.recommended_value, return OptionRecommendation(recommended_value=self.recommended_value,
level=self.level, option=self.option.clone()) level=self.level, option=self.option.clone())

View File

@ -3,7 +3,6 @@ __license__ = 'GPL 3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import re
from itertools import izip from itertools import izip
from calibre.customize import Plugin as _Plugin from calibre.customize import Plugin as _Plugin
@ -141,16 +140,13 @@ class OutputProfile(Plugin):
'if you want to produce a document intended to be read at a ' 'if you want to produce a document intended to be read at a '
'computer or on a range of devices.') 'computer or on a range of devices.')
# ADE dies an agonizing, long drawn out death if HTML files have more
# bytes than this.
flow_size = -1
# ADE runs screaming when it sees these characters
remove_special_chars = re.compile(u'[\u200b\u00ad]')
# ADE falls to the ground in a dead faint when it sees an <object>
remove_object_tags = True
# The image size for comics # The image size for comics
comic_screen_size = (584, 754) comic_screen_size = (584, 754)
@classmethod
def tags_to_string(cls, tags):
return ', '.join(tags)
class SonyReaderOutput(OutputProfile): class SonyReaderOutput(OutputProfile):
name = 'Sony Reader' name = 'Sony Reader'
@ -158,7 +154,6 @@ class SonyReaderOutput(OutputProfile):
description = _('This profile is intended for the SONY PRS line. ' description = _('This profile is intended for the SONY PRS line. '
'The 500/505/700 etc.') 'The 500/505/700 etc.')
flow_size = 270000
screen_size = (600, 775) screen_size = (600, 775)
dpi = 168.451 dpi = 168.451
fbase = 12 fbase = 12
@ -236,6 +231,11 @@ class KindleOutput(OutputProfile):
fbase = 16 fbase = 16
fsizes = [12, 12, 14, 16, 18, 20, 22, 24] fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
@classmethod
def tags_to_string(cls, tags):
return 'ttt '.join(tags)+'ttt '
output_profiles = [OutputProfile, SonyReaderOutput, MSReaderOutput, output_profiles = [OutputProfile, SonyReaderOutput, MSReaderOutput,
MobipocketOutput, HanlinV3Output, CybookG3Output, KindleOutput, MobipocketOutput, HanlinV3Output, CybookG3Output, KindleOutput,
SonyReaderLandscapeOutput] SonyReaderLandscapeOutput]

View File

@ -108,6 +108,11 @@ def debug_device_driver():
drives.append((str(drive.PNPDeviceID), 'No mount points found')) drives.append((str(drive.PNPDeviceID), 'No mount points found'))
for drive in drives: for drive in drives:
print '\t', drive print '\t', drive
if isosx:
from calibre.devices.usbms.device import Device
raw = Device.run_ioreg()
open('/tmp/ioreg.txt', 'wb').write(raw)
print 'ioreg output saved to /tmp/ioreg.txt'
from calibre.devices import devices from calibre.devices import devices
for dev in devices(): for dev in devices():
print 'Looking for', dev.__name__ print 'Looking for', dev.__name__

View File

@ -231,13 +231,19 @@ class Device(DeviceConfig, DevicePlugin):
self._card_a_prefix = drives.get('carda', None) self._card_a_prefix = drives.get('carda', None)
self._card_b_prefix = drives.get('cardb', None) self._card_b_prefix = drives.get('cardb', None)
@classmethod
def run_ioreg(cls, raw=None):
if raw is not None:
return raw
ioreg = '/usr/sbin/ioreg'
if not os.access(ioreg, os.X_OK):
ioreg = 'ioreg'
return subprocess.Popen((ioreg+' -w 0 -S -c IOMedia').split(),
stdout=subprocess.PIPE).communicate()[0]
def get_osx_mountpoints(self, raw=None): def get_osx_mountpoints(self, raw=None):
if raw is None: raw = self.run_ioreg(raw)
ioreg = '/usr/sbin/ioreg'
if not os.access(ioreg, os.X_OK):
ioreg = 'ioreg'
raw = subprocess.Popen((ioreg+' -w 0 -S -c IOMedia').split(),
stdout=subprocess.PIPE).communicate()[0]
lines = raw.splitlines() lines = raw.splitlines()
names = {} names = {}

View File

@ -19,6 +19,10 @@ def supported_input_formats():
fmts.add(x) fmts.add(x)
return fmts return fmts
INPUT_FORMAT_PREFERENCES = ['cbr', 'cbz', 'cbc', 'lit', 'mobi', 'prc', 'azw', 'fb2', 'html',
'rtf', 'pdf', 'txt', 'pdb']
OUTPUT_FORMAT_PREFERENCES = ['epub', 'mobi', 'lit', 'pdf', 'pdb', 'txt']
class OptionValues(object): class OptionValues(object):
pass pass
@ -114,7 +118,7 @@ OptionRecommendation(name='font_size_mapping',
), ),
OptionRecommendation(name='line_height', OptionRecommendation(name='line_height',
recommended_value=None, level=OptionRecommendation.LOW, recommended_value=0, level=OptionRecommendation.LOW,
help=_('The line height in pts. Controls spacing between consecutive ' help=_('The line height in pts. Controls spacing between consecutive '
'lines of text. By default no line height manipulation is ' 'lines of text. By default no line height manipulation is '
'performed.' 'performed.'
@ -463,6 +467,12 @@ OptionRecommendation(name='list_recipes',
if rec.option == name: if rec.option == name:
return rec return rec
def get_option_help(self, name):
rec = self.get_option_by_name(name)
help = getattr(rec, 'help', None)
if help is not None:
return help.replace('%default', str(rec.recommended_value))
def merge_plugin_recommendations(self): def merge_plugin_recommendations(self):
for source in (self.input_plugin, self.output_plugin): for source in (self.input_plugin, self.output_plugin):
for name, val, level in source.recommendations: for name, val, level in source.recommendations:
@ -598,7 +608,7 @@ OptionRecommendation(name='list_recipes',
from calibre.ebooks.oeb.transforms.flatcss import CSSFlattener from calibre.ebooks.oeb.transforms.flatcss import CSSFlattener
fbase = self.opts.base_font_size fbase = self.opts.base_font_size
if fbase == 0: if fbase < 1e-4:
fbase = float(self.opts.dest.fbase) fbase = float(self.opts.dest.fbase)
fkey = self.opts.font_size_mapping fkey = self.opts.font_size_mapping
if fkey is None: if fkey is None:
@ -618,8 +628,11 @@ OptionRecommendation(name='list_recipes',
if self.output_plugin.file_type == 'lrf': if self.output_plugin.file_type == 'lrf':
self.opts.insert_blank_line = False self.opts.insert_blank_line = False
self.opts.remove_paragraph_spacing = False self.opts.remove_paragraph_spacing = False
line_height = self.opts.line_height
if line_height < 1e-4:
line_height = None
flattener = CSSFlattener(fbase=fbase, fkey=fkey, flattener = CSSFlattener(fbase=fbase, fkey=fkey,
lineh=self.opts.line_height, lineh=line_height,
untable=self.output_plugin.file_type in ('mobi','lit'), untable=self.output_plugin.file_type in ('mobi','lit'),
unfloat=self.output_plugin.file_type in ('mobi', 'lit')) unfloat=self.output_plugin.file_type in ('mobi', 'lit'))
flattener(self.oeb, self.opts) flattener(self.oeb, self.opts)

View File

@ -1,21 +0,0 @@
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'Convert a comic in CBR/CBZ format to epub'
import sys
from functools import partial
from calibre.ebooks.lrf.comic.convert_from import do_convert, option_parser, config, main as _main
convert = partial(do_convert, output_format='epub')
main = partial(_main, output_format='epub')
if __name__ == '__main__':
sys.exit(main())
if False:
option_parser
config

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os, shutil import os, shutil, re
from urllib import unquote from urllib import unquote
from calibre.customize.conversion import OutputFormatPlugin from calibre.customize.conversion import OutputFormatPlugin
@ -42,6 +42,13 @@ class EPUBOutput(OutputFormatPlugin):
) )
), ),
OptionRecommendation(name='flow_size', recommended_value=260,
help=_('Split all HTML files larger than this size (in KB). '
'This is necessary as most EPUB readers cannot handle large '
'file sizes. The default of %defaultKB is the size required '
'for Adobe Digital Editions.')
),
]) ])
@ -104,7 +111,7 @@ class EPUBOutput(OutputFormatPlugin):
from calibre.ebooks.oeb.transforms.split import Split from calibre.ebooks.oeb.transforms.split import Split
split = Split(not self.opts.dont_split_on_page_breaks, split = Split(not self.opts.dont_split_on_page_breaks,
max_flow_size=self.opts.output_profile.flow_size max_flow_size=self.opts.flow_size*1024
) )
split(self.oeb, self.opts) split(self.oeb, self.opts)
@ -243,14 +250,12 @@ class EPUBOutput(OutputFormatPlugin):
br.tail = '' br.tail = ''
br.tail += sibling.tail br.tail += sibling.tail
for tag in XPath('//h:embed')(root):
if self.opts.output_profile.remove_object_tags: tag.getparent().remove(tag)
for tag in XPath('//h:embed')(root): for tag in XPath('//h:object')(root):
tag.getparent().remove(tag) if tag.get('type', '').lower().strip() in ('image/svg+xml',):
for tag in XPath('//h:object')(root): continue
if tag.get('type', '').lower().strip() in ('image/svg+xml',): tag.getparent().remove(tag)
continue
tag.getparent().remove(tag)
for tag in XPath('//h:title|//h:style')(root): for tag in XPath('//h:title|//h:style')(root):
if not tag.text: if not tag.text:
@ -276,5 +281,12 @@ class EPUBOutput(OutputFormatPlugin):
stylesheet.data.add('a[href] { color: blue; ' stylesheet.data.add('a[href] { color: blue; '
'text-decoration: underline; cursor:pointer; }') 'text-decoration: underline; cursor:pointer; }')
special_chars = re.compile(u'[\u200b\u00ad]')
for elem in root.iterdescendants():
if getattr(elem, 'text', False):
elem.text = special_chars.sub('', elem.text)
if getattr(elem, 'tail', False):
elem.tail = special_chars.sub('', elem.tail)

View File

@ -53,6 +53,7 @@ class LRFOptions(object):
self.lrs = False self.lrs = False
self.minimize_memory_usage = False self.minimize_memory_usage = False
self.autorotation = opts.enable_autorotation self.autorotation = opts.enable_autorotation
self.header_separation = (self.profile.dpi/72.) * opts.header_separation
for x in ('top', 'bottom', 'left', 'right'): for x in ('top', 'bottom', 'left', 'right'):
@ -60,7 +61,7 @@ class LRFOptions(object):
'margin_'+x)) 'margin_'+x))
for x in ('wordspace', 'header', 'header_format', for x in ('wordspace', 'header', 'header_format',
'header_separation', 'minimum_indent', 'serif_family', 'minimum_indent', 'serif_family',
'render_tables_as_images', 'sans_family', 'mono_family', 'render_tables_as_images', 'sans_family', 'mono_family',
'text_size_multiplier_for_rendered_tables'): 'text_size_multiplier_for_rendered_tables'):
setattr(self, x, getattr(opts, x)) setattr(self, x, getattr(opts, x))
@ -87,7 +88,7 @@ class LRFOutput(OutputFormatPlugin):
'and %t by the title. Default is %default') 'and %t by the title. Default is %default')
), ),
OptionRecommendation(name='header_separation', recommended_value=0, OptionRecommendation(name='header_separation', recommended_value=0,
help=_('Add extra spacing below the header. Default is %default px.') help=_('Add extra spacing below the header. Default is %default pt.')
), ),
OptionRecommendation(name='minimum_indent', recommended_value=0, OptionRecommendation(name='minimum_indent', recommended_value=0,
help=_('Minimum paragraph indent (the indent of the first line ' help=_('Minimum paragraph indent (the indent of the first line '
@ -99,7 +100,7 @@ class LRFOutput(OutputFormatPlugin):
'document has large or complex tables)') 'document has large or complex tables)')
), ),
OptionRecommendation(name='text_size_multiplier_for_rendered_tables', OptionRecommendation(name='text_size_multiplier_for_rendered_tables',
recommended_value=1, recommended_value=1.0,
help=_('Multiply the size of text in rendered tables by this ' help=_('Multiply the size of text in rendered tables by this '
'factor. Default is %default') 'factor. Default is %default')
), ),

View File

@ -1,70 +0,0 @@
'''
Convert any ebook format to Mobipocket.
'''
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net ' \
'and Marshall T. Vandegrift <llasram@gmail.com>'
__docformat__ = 'restructuredtext en'
import sys, os, glob, logging
from calibre.ebooks.epub.from_any import any2epub, formats, USAGE
from calibre.ebooks.epub import config as common_config
from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.mobi.writer import oeb2mobi, config as mobi_config
def config(defaults=None):
c = common_config(defaults=defaults, name='mobi')
c.remove_opt('profile')
mobic = mobi_config(defaults=defaults)
c.update(mobic)
return c
def option_parser(usage=USAGE):
usage = usage % ('Mobipocket', formats())
parser = config().option_parser(usage=usage)
return parser
def any2mobi(opts, path, notification=None):
ext = os.path.splitext(path)[1]
if not ext:
raise ValueError('Unknown file type: '+path)
ext = ext.lower()[1:]
if opts.output is None:
opts.output = os.path.splitext(os.path.basename(path))[0]+'.mobi'
opts.output = os.path.abspath(opts.output)
orig_output = opts.output
with TemporaryDirectory('_any2mobi') as tdir:
oebdir = os.path.join(tdir, 'oeb')
os.mkdir(oebdir)
opts.output = os.path.join(tdir, 'dummy.epub')
opts.profile = 'None'
opts.dont_split_on_page_breaks = True
orig_bfs = opts.base_font_size2
opts.base_font_size2 = 0
any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir)
opts.base_font_size2 = orig_bfs
opf = glob.glob(os.path.join(oebdir, '*.opf'))[0]
opts.output = orig_output
logging.getLogger('html2epub').info(_('Creating Mobipocket file from EPUB...'))
oeb2mobi(opts, opf)
def main(args=sys.argv):
parser = option_parser()
opts, args = parser.parse_args(args)
if len(args) < 2:
parser.print_help()
print 'No input file specified.'
return 1
any2mobi(opts, args[1])
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -1,44 +0,0 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
'''
import sys, os
from calibre.ebooks.lrf.comic.convert_from import do_convert, option_parser, \
ProgressBar, terminal_controller
from calibre.ebooks.mobi.from_any import config, any2mobi
from calibre.ptempfile import PersistentTemporaryFile
def convert(path_to_file, opts, notification=lambda m, p: p):
pt = PersistentTemporaryFile('_comic2mobi.epub')
pt.close()
orig_output = opts.output
opts.output = pt.name
do_convert(path_to_file, opts, notification=notification, output_format='epub')
opts = config('').parse()
if orig_output is None:
orig_output = os.path.splitext(path_to_file)[0]+'.mobi'
opts.output = orig_output
any2mobi(opts, pt.name)
def main(args=sys.argv):
parser = option_parser()
opts, args = parser.parse_args(args)
if len(args) < 2:
parser.print_help()
print '\nYou must specify a file to convert'
return 1
pb = ProgressBar(terminal_controller, _('Rendering comic pages...'),
no_progress_bar=opts.no_progress_bar or getattr(opts, 'no_process', False))
notification = pb.update
source = os.path.abspath(args[1])
convert(source, opts, notification=notification)
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -1,74 +0,0 @@
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
Convert feeds to MOBI ebook
'''
import sys, glob, os
from calibre.web.feeds.main import config as feeds2disk_config, USAGE, run_recipe
from calibre.ebooks.mobi.writer import config as oeb2mobi_config, oeb2mobi
from calibre.ptempfile import TemporaryDirectory
from calibre import strftime, sanitize_file_name
def config(defaults=None):
c = feeds2disk_config(defaults=defaults)
c.remove('lrf')
c.remove('epub')
c.remove('mobi')
c.remove('output_dir')
c.update(oeb2mobi_config(defaults=defaults))
c.remove('encoding')
c.remove('source_profile')
c.add_opt('output', ['-o', '--output'], default=None,
help=_('Output file. Default is derived from input filename.'))
return c
def option_parser():
c = config()
return c.option_parser(usage=USAGE)
def convert(opts, recipe_arg, notification=None):
opts.lrf = False
opts.epub = False
opts.mobi = True
if opts.debug:
opts.verbose = 2
parser = option_parser()
with TemporaryDirectory('_feeds2mobi') as tdir:
opts.output_dir = tdir
recipe = run_recipe(opts, recipe_arg, parser, notification=notification)
c = config()
recipe_opts = c.parse_string(recipe.oeb2mobi_options)
c.smart_update(recipe_opts, opts)
opts = recipe_opts
opf = glob.glob(os.path.join(tdir, '*.opf'))
if not opf:
raise Exception('Downloading of recipe: %s failed'%recipe_arg)
opf = opf[0]
if opts.output is None:
fname = recipe.title + strftime(recipe.timefmt) + '.mobi'
opts.output = os.path.join(os.getcwd(), sanitize_file_name(fname))
print 'Generating MOBI...'
opts.encoding = 'utf-8'
opts.source_profile = 'Browser'
oeb2mobi(opts, opf)
def main(args=sys.argv, notification=None, handler=None):
parser = option_parser()
opts, args = parser.parse_args(args)
if len(args) != 2 and opts.feeds is None:
parser.print_help()
return 1
recipe_arg = args[1] if len(args) > 1 else None
convert(opts, recipe_arg, notification=notification)
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -70,9 +70,8 @@ class Jacket(object):
tags = map(unicode, self.oeb.metadata.subject) tags = map(unicode, self.oeb.metadata.subject)
except: except:
tags = [] tags = []
tags = u'/'.join(tags)
if tags: if tags:
tags = '<b>Tags: </b>' + u'/%s/'%tags tags = '<b>Tags: </b>' + self.opts.dest.tags_to_string(tags)
else: else:
tags = '' tags = ''
try: try:

View File

@ -8,7 +8,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>' __copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os, re, sys, struct, zlib import os, re, struct, zlib
from calibre import CurrentDir from calibre import CurrentDir
from calibre.ebooks import DRMError from calibre.ebooks import DRMError
@ -30,7 +30,7 @@ class HeaderRecord(object):
def __init__(self, raw): def __init__(self, raw):
self.version, = struct.unpack('>H', raw[0:2]) self.version, = struct.unpack('>H', raw[0:2])
self.non_text_offset, = struct.unpack('>H', raw[12:14]) self.non_text_offset, = struct.unpack('>H', raw[12:14])
self.footnote_rec, = struct.unpack('>H', raw[28:30]) self.footnote_rec, = struct.unpack('>H', raw[28:30])
self.sidebar_rec, = struct.unpack('>H', raw[30:32]) self.sidebar_rec, = struct.unpack('>H', raw[30:32])
self.bookmark_offset, = struct.unpack('>H', raw[32:34]) self.bookmark_offset, = struct.unpack('>H', raw[32:34])
@ -39,17 +39,17 @@ class HeaderRecord(object):
self.footnote_offset, = struct.unpack('>H', raw[48:50]) self.footnote_offset, = struct.unpack('>H', raw[48:50])
self.sidebar_offset, = struct.unpack('>H', raw[50:52]) self.sidebar_offset, = struct.unpack('>H', raw[50:52])
self.last_data_offset, = struct.unpack('>H', raw[52:54]) self.last_data_offset, = struct.unpack('>H', raw[52:54])
self.num_text_pages = self.non_text_offset - 1 self.num_text_pages = self.non_text_offset - 1
self.num_image_pages = self.metadata_offset - self.image_data_offset self.num_image_pages = self.metadata_offset - self.image_data_offset
class Reader(FormatReader): class Reader(FormatReader):
def __init__(self, header, stream, log, encoding=None): def __init__(self, header, stream, log, encoding=None):
self.log = log self.log = log
self.encoding = encoding self.encoding = encoding
self.sections = [] self.sections = []
for i in range(header.num_sections): for i in range(header.num_sections):
self.sections.append(header.section_data(i)) self.sections.append(header.section_data(i))
@ -61,17 +61,17 @@ class Reader(FormatReader):
raise DRMError('eReader DRM is not supported.') raise DRMError('eReader DRM is not supported.')
else: else:
raise EreaderError('Unknown book version %i.' % self.header_record.version) raise EreaderError('Unknown book version %i.' % self.header_record.version)
def section_data(self, number): def section_data(self, number):
return self.sections[number] return self.sections[number]
def decompress_text(self, number): def decompress_text(self, number):
if self.header_record.version == 2: if self.header_record.version == 2:
return decompress_doc(self.section_data(number)).decode('cp1252' if self.encoding is None else self.encoding) return decompress_doc(self.section_data(number)).decode('cp1252' if self.encoding is None else self.encoding)
if self.header_record.version == 10: if self.header_record.version == 10:
return zlib.decompress(self.section_data(number)).decode('cp1252' if self.encoding is None else self.encoding) return zlib.decompress(self.section_data(number)).decode('cp1252' if self.encoding is None else self.encoding)
def get_image(self, number): def get_image(self, number):
if number < self.header_record.image_data_offset or number > self.header_record.image_data_offset + self.header_record.num_image_pages - 1: if number < self.header_record.image_data_offset or number > self.header_record.image_data_offset + self.header_record.num_image_pages - 1:
return 'empty', '' return 'empty', ''
@ -79,7 +79,7 @@ class Reader(FormatReader):
name = data[4:4+32].strip('\x00') name = data[4:4+32].strip('\x00')
img = data[62:] img = data[62:]
return name, img return name, img
def get_text_page(self, number): def get_text_page(self, number):
''' '''
Only palmdoc and zlib compressed are supported. The text is Only palmdoc and zlib compressed are supported. The text is
@ -88,21 +88,21 @@ class Reader(FormatReader):
''' '''
if number not in range(1, self.header_record.num_text_pages + 1): if number not in range(1, self.header_record.num_text_pages + 1):
return '' return ''
return self.decompress_text(number) return self.decompress_text(number)
def extract_content(self, output_dir): def extract_content(self, output_dir):
output_dir = os.path.abspath(output_dir) output_dir = os.path.abspath(output_dir)
if not os.path.exists(output_dir): if not os.path.exists(output_dir):
os.makedirs(output_dir) os.makedirs(output_dir)
html = u'<html><head><title></title></head><body>' html = u'<html><head><title></title></head><body>'
for i in range(1, self.header_record.num_text_pages + 1): for i in range(1, self.header_record.num_text_pages + 1):
self.log.debug('Extracting text page %i' % i) self.log.debug('Extracting text page %i' % i)
html += pml_to_html(self.get_text_page(i)) html += pml_to_html(self.get_text_page(i))
if self.header_record.footnote_rec > 0: if self.header_record.footnote_rec > 0:
html += '<br /><h1>%s</h1>' % _('Footnotes') html += '<br /><h1>%s</h1>' % _('Footnotes')
footnoteids = re.findall('\w+(?=\x00)', self.section_data(self.header_record.footnote_offset).decode('cp1252' if self.encoding is None else self.encoding)) footnoteids = re.findall('\w+(?=\x00)', self.section_data(self.header_record.footnote_offset).decode('cp1252' if self.encoding is None else self.encoding))
@ -110,8 +110,8 @@ class Reader(FormatReader):
self.log.debug('Extracting footnote page %i' % i) self.log.debug('Extracting footnote page %i' % i)
html += '<dl>' html += '<dl>'
html += footnote_sidebar_to_html(footnoteids[fid], self.decompress_text(i)) html += footnote_sidebar_to_html(footnoteids[fid], self.decompress_text(i))
html += '</dl>' html += '</dl>'
if self.header_record.sidebar_rec > 0: if self.header_record.sidebar_rec > 0:
html += '<br /><h1>%s</h1>' % _('Sidebar') html += '<br /><h1>%s</h1>' % _('Sidebar')
sidebarids = re.findall('\w+(?=\x00)', self.section_data(self.header_record.sidebar_offset).decode('cp1252' if self.encoding is None else self.encoding)) sidebarids = re.findall('\w+(?=\x00)', self.section_data(self.header_record.sidebar_offset).decode('cp1252' if self.encoding is None else self.encoding))
@ -120,9 +120,9 @@ class Reader(FormatReader):
html += '<dl>' html += '<dl>'
html += footnote_sidebar_to_html(sidebarids[sid], self.decompress_text(i)) html += footnote_sidebar_to_html(sidebarids[sid], self.decompress_text(i))
html += '</dl>' html += '</dl>'
html += '</body></html>' html += '</body></html>'
with CurrentDir(output_dir): with CurrentDir(output_dir):
with open('index.html', 'wb') as index: with open('index.html', 'wb') as index:
self.log.debug('Writing text to index.html') self.log.debug('Writing text to index.html')
@ -138,19 +138,19 @@ class Reader(FormatReader):
with open(name, 'wb') as imgf: with open(name, 'wb') as imgf:
self.log.debug('Writing image %s to images/' % name) self.log.debug('Writing image %s to images/' % name)
imgf.write(img) imgf.write(img)
opf_path = self.create_opf(output_dir, images) opf_path = self.create_opf(output_dir, images)
return opf_path return opf_path
def create_opf(self, output_dir, images): def create_opf(self, output_dir, images):
mi = MetaInformation(None, None) mi = MetaInformation(None, None)
with CurrentDir(output_dir): with CurrentDir(output_dir):
opf = OPFCreator(output_dir, mi) opf = OPFCreator(output_dir, mi)
manifest = [('index.html', None)] manifest = [('index.html', None)]
for i in images: for i in images:
manifest.append((os.path.join('images/', i), None)) manifest.append((os.path.join('images/', i), None))
@ -158,21 +158,21 @@ class Reader(FormatReader):
opf.create_spine(['index.html']) opf.create_spine(['index.html'])
with open('metadata.opf', 'wb') as opffile: with open('metadata.opf', 'wb') as opffile:
opf.render(opffile) opf.render(opffile)
return os.path.join(output_dir, 'metadata.opf') return os.path.join(output_dir, 'metadata.opf')
def dump_pml(self): def dump_pml(self):
''' '''
This is primarily used for debugging and 3rd party tools to This is primarily used for debugging and 3rd party tools to
get the plm markup that comprises the text in the file. get the plm markup that comprises the text in the file.
''' '''
pml = '' pml = ''
for i in range(1, self.header_record.num_text_pages + 1): for i in range(1, self.header_record.num_text_pages + 1):
pml += self.get_text_page(i) pml += self.get_text_page(i)
return pml return pml
def dump_images(self, output_dir): def dump_images(self, output_dir):
''' '''
This is primarily used for debugging and 3rd party tools to This is primarily used for debugging and 3rd party tools to
@ -181,7 +181,7 @@ class Reader(FormatReader):
if not os.path.exists(output_dir): if not os.path.exists(output_dir):
os.makedirs(output_dir) os.makedirs(output_dir)
with CurrentDir(output_dir): with CurrentDir(output_dir):
for i in range(0, self.header_record.num_image_pages): for i in range(0, self.header_record.num_image_pages):
name, img = self.get_image(self.header_record.image_data_offset + i) name, img = self.get_image(self.header_record.image_data_offset + i)
with open(name, 'wb') as imgf: with open(name, 'wb') as imgf:

View File

@ -8,7 +8,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>' __copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os, re, struct, time import re, struct, time
class PdbHeaderReader(object): class PdbHeaderReader(object):
@ -53,7 +53,8 @@ class PdbHeaderReader(object):
start = self.section_offset(number) start = self.section_offset(number)
if number == self.num_sections -1: if number == self.num_sections -1:
end = os.stat(self.stream.name).st_size self.stream.seek(0, 2)
end = self.stream.tell()
else: else:
end = self.section_offset(number + 1) end = self.section_offset(number + 1)
self.stream.seek(start) self.stream.seek(start)
@ -65,18 +66,18 @@ class PdbHeaderBuilder(object):
def __init__(self, identity, title): def __init__(self, identity, title):
self.identity = identity.ljust(3, '\x00')[:8] self.identity = identity.ljust(3, '\x00')[:8]
self.title = re.sub('[^-A-Za-z0-9]+', '_', title).ljust(32, '\x00')[:32] self.title = re.sub('[^-A-Za-z0-9]+', '_', title).ljust(32, '\x00')[:32]
def build_header(self, section_lengths, out_stream): def build_header(self, section_lengths, out_stream):
''' '''
section_lengths = Lenght of each section in file. section_lengths = Lenght of each section in file.
''' '''
now = int(time.time()) now = int(time.time())
nrecords = len(section_lengths) nrecords = len(section_lengths)
out_stream.write(self.title + struct.pack('>HHIIIIII', 0, 0, now, now, 0, 0, 0, 0)) out_stream.write(self.title + struct.pack('>HHIIIIII', 0, 0, now, now, 0, 0, 0, 0))
out_stream.write(self.identity + struct.pack('>IIH', nrecords, 0, nrecords)) out_stream.write(self.identity + struct.pack('>IIH', nrecords, 0, nrecords))
offset = 78 + (8 * nrecords) + 2 offset = 78 + (8 * nrecords) + 2
for id, record in enumerate(section_lengths): for id, record in enumerate(section_lengths):
out_stream.write(struct.pack('>LBBBB', long(offset), 0, 0, 0, 0)) out_stream.write(struct.pack('>LBBBB', long(offset), 0, 0, 0, 0))

View File

@ -15,7 +15,6 @@ import os, glob
from calibre.customize.conversion import OutputFormatPlugin, \ from calibre.customize.conversion import OutputFormatPlugin, \
OptionRecommendation OptionRecommendation
from calibre.ebooks.oeb.output import OEBOutput
from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata.opf2 import OPF
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata
@ -52,24 +51,24 @@ class PDFOutput(OutputFormatPlugin):
self.input_plugin, self.opts, self.log = input_plugin, opts, log self.input_plugin, self.opts, self.log = input_plugin, opts, log
self.output_path = output_path self.output_path = output_path
self.metadata = oeb_book.metadata self.metadata = oeb_book.metadata
if input_plugin.is_image_collection: if input_plugin.is_image_collection:
self.convert_images(input_plugin.get_images()) self.convert_images(input_plugin.get_images())
else: else:
self.convert_text(oeb_book) self.convert_text(oeb_book)
def convert_images(self, images): def convert_images(self, images):
self.write(ImagePDFWriter, images) self.write(ImagePDFWriter, images)
def convert_text(self, oeb_book): def convert_text(self, oeb_book):
with TemporaryDirectory('_pdf_out') as oeb_dir: with TemporaryDirectory('_pdf_out') as oeb_dir:
from calibre.customize.ui import plugin_for_output_format from calibre.customize.ui import plugin_for_output_format
oeb_output = plugin_for_output_format('oeb') oeb_output = plugin_for_output_format('oeb')
oeb_output.convert(oeb_book, oeb_dir, self.input_plugin, self.opts, self.log) oeb_output.convert(oeb_book, oeb_dir, self.input_plugin, self.opts, self.log)
opfpath = glob.glob(os.path.join(oeb_dir, '*.opf'))[0] opfpath = glob.glob(os.path.join(oeb_dir, '*.opf'))[0]
opf = OPF(opfpath, os.path.dirname(opfpath)) opf = OPF(opfpath, os.path.dirname(opfpath))
self.write(PDFWriter, [s.path for s in opf.spine]) self.write(PDFWriter, [s.path for s in opf.spine])
def write(self, Writer, items): def write(self, Writer, items):

View File

@ -0,0 +1,234 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
from PyQt4.Qt import QWidget, QSpinBox, QDoubleSpinBox, QLineEdit, QTextEdit, \
QCheckBox, QComboBox, Qt, QIcon, SIGNAL
from calibre.customize.conversion import OptionRecommendation
from calibre.utils.config import config_dir
from calibre.utils.lock import ExclusiveFile
from calibre import sanitize_file_name
config_dir = os.path.join(config_dir, 'conversion')
if not os.path.exists(config_dir):
os.makedirs(config_dir)
def name_to_path(name):
return os.path.join(config_dir, sanitize_file_name(name)+'.py')
def save_defaults(name, recs):
path = name_to_path(name)
raw = str(recs)
with open(path, 'wb'):
pass
with ExclusiveFile(path) as f:
f.write(raw)
save_defaults_ = save_defaults
def load_defaults(name):
path = name_to_path(name)
if not os.path.exists(path):
open(path, 'wb').close()
with ExclusiveFile(path) as f:
raw = f.read()
r = GuiRecommendations()
if raw:
r.from_string(raw)
return r
def save_specifics(db, book_id, recs):
raw = str(recs)
db.set_conversion_options(book_id, 'PIPE', raw)
def load_specifics(db, book_id):
raw = db.conversion_options(book_id, 'PIPE')
r = GuiRecommendations()
if raw:
r.from_string(raw)
return r
class GuiRecommendations(dict):
def __new__(cls, *args):
dict.__new__(cls)
obj = super(GuiRecommendations, cls).__new__(cls, *args)
obj.disabled_options = set([])
return obj
def to_recommendations(self, level=OptionRecommendation.LOW):
ans = []
for key, val in self.items():
ans.append((key, val, level))
return ans
def __str__(self):
ans = ['{']
for key, val in self.items():
ans.append('\t'+repr(key)+' : '+repr(val)+',')
ans.append('}')
return '\n'.join(ans)
def from_string(self, raw):
try:
d = eval(raw)
except SyntaxError:
d = None
if d:
self.update(d)
def merge_recommendations(self, get_option, level, options):
for name in options:
opt = get_option(name)
if opt is None: continue
if opt.level == OptionRecommendation.HIGH:
self[name] = opt.recommended_value
self.disabled_options.add(name)
elif opt.level > level or name not in self:
self[name] = opt.recommended_value
class Widget(QWidget):
TITLE = _('Unknown')
ICON = ':/images/config.svg'
HELP = ''
def __init__(self, parent, name, options):
QWidget.__init__(self, parent)
self.setupUi(self)
self._options = options
self._name = name
self._icon = QIcon(self.ICON)
for name in self._options:
if not hasattr(self, 'opt_'+name):
raise Exception('Option %s missing in %s'%(name,
self.__class__.__name__))
def initialize_options(self, get_option, get_help, db=None, book_id=None):
'''
:param get_option: A callable that takes one argument: the option name
and returns the correspoing OptionRecommendation.
:param get_help: A callable that takes the option name and return a help
string.
'''
defaults = load_defaults(self._name)
defaults.merge_recommendations(get_option, OptionRecommendation.LOW,
self._options)
if db is not None:
specifics = load_specifics(db, book_id)
specifics.merge_recommendations(get_option, OptionRecommendation.HIGH,
self._options)
defaults.update(specifics)
self.apply_recommendations(defaults)
self.setup_help(get_help)
def commit_options(self, save_defaults=False):
recs = self.create_recommendations()
if save_defaults:
save_defaults_(self._name, recs)
return recs
def create_recommendations(self):
recs = GuiRecommendations()
for name in self._options:
gui_opt = getattr(self, 'opt_'+name, None)
if gui_opt is None: continue
recs[name] = self.get_value(gui_opt)
return recs
def apply_recommendations(self, recs):
for name, val in recs.items():
gui_opt = getattr(self, 'opt_'+name, None)
if gui_opt is None: continue
self.set_value(gui_opt, val)
if name in getattr(recs, 'disabled_options', []):
gui_opt.setDisabled(True)
def get_value(self, g):
ret = self.get_value_handler(g)
if ret != 'this is a dummy return value, xcswx1avcx4x':
return ret
if isinstance(g, (QSpinBox, QDoubleSpinBox)):
return g.value()
elif isinstance(g, (QLineEdit, QTextEdit)):
func = getattr(g, 'toPlainText', getattr(g, 'text', None))()
ans = unicode(func).strip()
if not ans:
ans = None
return ans
elif isinstance(g, QComboBox):
return unicode(g.currentText())
elif isinstance(g, QCheckBox):
return bool(g.isChecked())
else:
raise Exception('Can\'t get value from %s'%type(g))
def set_value(self, g, val):
if self.set_value_handler(g, val):
return
if isinstance(g, (QSpinBox, QDoubleSpinBox)):
g.setValue(val)
elif isinstance(g, (QLineEdit, QTextEdit)):
if not val: val = ''
getattr(g, 'setPlainText', g.setText)(val)
getattr(g, 'setCursorPosition', lambda x: x)(0)
elif isinstance(g, QComboBox) and val:
idx = g.findText(val, Qt.MatchFixedString)
if idx < 0:
g.addItem(val)
idx = g.findText(val, Qt.MatchFixedString)
g.setCurrentIndex(idx)
elif isinstance(g, QCheckBox):
g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked)
else:
raise Exception('Can\'t set value %s in %s'%(repr(val), type(g)))
self.post_set_value(g, val)
def set_help(self, msg):
if msg and getattr(msg, 'strip', lambda:True)():
self.emit(SIGNAL('set_help(PyQt_PyObject)'), msg)
def setup_help(self, help_provider):
for name in self._options:
g = getattr(self, 'opt_'+name, None)
if g is None:
continue
help = help_provider(name)
if not help: continue
g._help = help
g.setToolTip(help.replace('<', '&lt;').replace('>', '&gt;'))
g.setWhatsThis(help.replace('<', '&lt;').replace('>', '&gt;'))
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))
def set_value_handler(self, g, val):
return False
def post_set_value(self, g, val):
pass
def get_value_handler(self, g):
return 'this is a dummy return value, xcswx1avcx4x'
def post_get_value(self, g):
pass
def commit(self, save_defaults=False):
return self.commit_options(save_defaults)
def config_title(self):
return self.TITLE
def config_icon(self):
return self._icon

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.convert.comic_input_ui import Ui_Form
from calibre.gui2.convert import Widget
class PluginWidget(Widget, Ui_Form):
TITLE = _('Comic Input')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'comic_input',
['colors', 'dont_normalize', 'keep_aspect_ratio', 'right2left',
'despeckle', 'no_sort', 'no_process', 'landscape',
'dont_sharpen', 'disable_trim', 'wide']
)
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)
self.opt_no_process.toggle()
self.opt_no_process.toggle()

View File

@ -0,0 +1,304 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>599</width>
<height>305</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Number of Colors:</string>
</property>
<property name="buddy">
<cstring>opt_colors</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="opt_colors">
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>3200000</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="opt_dont_normalize">
<property name="text">
<string>Disable &amp;normalize</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="opt_keep_aspect_ratio">
<property name="text">
<string>Keep &amp;aspect ratio</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="opt_dont_sharpen">
<property name="text">
<string>Disable &amp;Sharpening</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="opt_disable_trim">
<property name="text">
<string>Disable &amp;Trimming</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="opt_wide">
<property name="text">
<string>&amp;Wide</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="opt_landscape">
<property name="text">
<string>&amp;Landscape</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QCheckBox" name="opt_right2left">
<property name="text">
<string>&amp;Right to left</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="opt_no_sort">
<property name="text">
<string>Don't so&amp;rt</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QCheckBox" name="opt_despeckle">
<property name="text">
<string>De&amp;speckle</string>
</property>
</widget>
</item>
<item row="11" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="opt_no_process">
<property name="text">
<string>&amp;Disable comic processing</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>label_3</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>184</x>
<y>11</y>
</hint>
<hint type="destinationlabel">
<x>210</x>
<y>44</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_colors</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>284</x>
<y>17</y>
</hint>
<hint type="destinationlabel">
<x>371</x>
<y>39</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_dont_normalize</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>107</x>
<y>15</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>67</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_keep_aspect_ratio</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>171</x>
<y>11</y>
</hint>
<hint type="destinationlabel">
<x>38</x>
<y>98</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_dont_sharpen</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>126</x>
<y>12</y>
</hint>
<hint type="destinationlabel">
<x>110</x>
<y>124</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_disable_trim</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>153</x>
<y>5</y>
</hint>
<hint type="destinationlabel">
<x>67</x>
<y>155</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_wide</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>164</x>
<y>13</y>
</hint>
<hint type="destinationlabel">
<x>84</x>
<y>180</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_landscape</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>129</x>
<y>11</y>
</hint>
<hint type="destinationlabel">
<x>145</x>
<y>209</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_right2left</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>183</x>
<y>15</y>
</hint>
<hint type="destinationlabel">
<x>52</x>
<y>225</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_no_sort</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>162</x>
<y>11</y>
</hint>
<hint type="destinationlabel">
<x>105</x>
<y>256</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_despeckle</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>254</x>
<y>19</y>
</hint>
<hint type="destinationlabel">
<x>180</x>
<y>296</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.convert.epub_output_ui import Ui_Form
from calibre.gui2.convert import Widget
class PluginWidget(Widget, Ui_Form):
TITLE = _('EPUB Output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'epub_output',
['dont_split_on_page_breaks', 'flow_size']
)
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="opt_dont_split_on_page_breaks">
<property name="text">
<string>Do not &amp;split on page breaks</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Split files &amp;larger than:</string>
</property>
<property name="buddy">
<cstring>opt_flow_size</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="opt_flow_size">
<property name="suffix">
<string> KB</string>
</property>
<property name="minimum">
<number>100</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
<property name="singleStep">
<number>20</number>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>262</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.convert.look_and_feel_ui import Ui_Form
from calibre.gui2.convert import Widget
class LookAndFeelWidget(Widget, Ui_Form):
TITLE = _('Look & Feel')
ICON = ':/images/lookfeel.svg'
HELP = _('Control the look and feel of the output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'look_and_feel',
['dont_justify', 'extra_css', 'base_font_size',
'font_size_mapping', 'insert_metadata', 'line_height',
'linearize_tables', 'remove_first_image',
'remove_paragraph_spacing', 'input_encoding']
)
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Base &amp;font size:</string>
</property>
<property name="buddy">
<cstring>opt_base_font_size</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QDoubleSpinBox" name="opt_base_font_size">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>30.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>15.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Line &amp;height:</string>
</property>
<property name="buddy">
<cstring>opt_line_height</cstring>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QDoubleSpinBox" name="opt_line_height">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="opt_remove_paragraph_spacing">
<property name="text">
<string>Remove &amp;spacing between paragraphs</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="opt_dont_justify">
<property name="text">
<string>No text &amp;justification</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="opt_linearize_tables">
<property name="text">
<string>&amp;Linearize tables</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="opt_remove_first_image">
<property name="text">
<string>Remove &amp;first image from source file</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Font size &amp;key:</string>
</property>
<property name="buddy">
<cstring>opt_font_size_mapping</cstring>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="opt_font_size_mapping"/>
</item>
<item row="8" column="0" colspan="2">
<widget class="QCheckBox" name="opt_insert_metadata">
<property name="text">
<string>Insert &amp;metadata at start of book</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Input character &amp;encoding</string>
</property>
<property name="buddy">
<cstring>opt_input_encoding</cstring>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="opt_input_encoding"/>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Extra &amp;CSS</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QTextEdit" name="opt_extra_css"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../work/calibre-pluginize/src/calibre/gui2/images.qrc"/>
<include location="../images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.convert.lrf_output_ui import Ui_Form
from calibre.gui2.convert import Widget
from calibre.gui2.widgets import FontFamilyModel
font_family_model = None
class PluginWidget(Widget, Ui_Form):
TITLE = _('LRF Output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'lrf_output',
['wordspace', 'header', 'header_format',
'minimum_indent', 'serif_family',
'render_tables_as_images', 'sans_family', 'mono_family',
'text_size_multiplier_for_rendered_tables', 'autorotation',
'header_separation', 'minimum_indent']
)
self.db, self.book_id = db, book_id
global font_family_model
if font_family_model is None:
font_family_model = FontFamilyModel()
self.font_family_model = font_family_model
self.opt_serif_family.setModel(self.font_family_model)
self.opt_sans_family.setModel(self.font_family_model)
self.opt_mono_family.setModel(self.font_family_model)
self.initialize_options(get_option, get_help, db, book_id)
self.opt_header.toggle(), self.opt_header.toggle()
self.opt_render_tables_as_images.toggle()
self.opt_render_tables_as_images.toggle()
def set_value_handler(self, g, val):
if val is None and unicode(g.objectName()) in ('opt_serif_family',
'opt_sans_family', 'opt_mono_family'):
g.setCurrentIndex(0)
return True
return False

View File

@ -0,0 +1,304 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>588</width>
<height>481</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="opt_autorotation">
<property name="text">
<string>Enable &amp;autorotation of wide images</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Wordspace:</string>
</property>
<property name="buddy">
<cstring>opt_wordspace</cstring>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="opt_wordspace">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>20.000000000000000</double>
</property>
<property name="value">
<double>2.500000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Minimum para. &amp;indent:</string>
</property>
<property name="buddy">
<cstring>opt_minimum_indent</cstring>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="opt_minimum_indent">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="opt_render_tables_as_images">
<property name="text">
<string>Render &amp;tables as images</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Text size multiplier for text in rendered tables:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="opt_text_size_multiplier_for_rendered_tables"/>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="opt_header">
<property name="text">
<string>Add &amp;header</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Header &amp;separation:</string>
</property>
<property name="buddy">
<cstring>opt_header_separation</cstring>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="opt_header_separation">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Header &amp;format:</string>
</property>
<property name="buddy">
<cstring>opt_header_format</cstring>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLineEdit" name="opt_header_format"/>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>&amp;Embed fonts</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Serif font family:</string>
</property>
<property name="buddy">
<cstring>opt_serif_family</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>S&amp;ans-serif font family:</string>
</property>
<property name="buddy">
<cstring>opt_sans_family</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>&amp;Monospaced font family:</string>
</property>
<property name="buddy">
<cstring>opt_mono_family</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="opt_serif_family"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="opt_sans_family"/>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="opt_mono_family"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>opt_render_tables_as_images</sender>
<signal>toggled(bool)</signal>
<receiver>opt_text_size_multiplier_for_rendered_tables</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>76</x>
<y>80</y>
</hint>
<hint type="destinationlabel">
<x>418</x>
<y>105</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_header</sender>
<signal>toggled(bool)</signal>
<receiver>opt_header_separation</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>346</x>
<y>144</y>
</hint>
<hint type="destinationlabel">
<x>408</x>
<y>167</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_header</sender>
<signal>toggled(bool)</signal>
<receiver>opt_header_format</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>124</x>
<y>138</y>
</hint>
<hint type="destinationlabel">
<x>230</x>
<y>208</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_render_tables_as_images</sender>
<signal>toggled(bool)</signal>
<receiver>label_3</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>96</x>
<y>76</y>
</hint>
<hint type="destinationlabel">
<x>31</x>
<y>113</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_header</sender>
<signal>toggled(bool)</signal>
<receiver>label_4</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>67</x>
<y>144</y>
</hint>
<hint type="destinationlabel">
<x>72</x>
<y>165</y>
</hint>
</hints>
</connection>
<connection>
<sender>opt_header</sender>
<signal>toggled(bool)</signal>
<receiver>label_5</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>107</x>
<y>141</y>
</hint>
<hint type="destinationlabel">
<x>102</x>
<y>211</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,161 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, uuid
from PyQt4.Qt import QPixmap, SIGNAL
from calibre.gui2 import choose_images, error_dialog, pixmap_to_data
from calibre.gui2.convert.metadata_ui import Ui_Form
from calibre.ebooks.metadata import authors_to_string, string_to_authors, \
MetaInformation
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ptempfile import PersistentTemporaryFile
from calibre.gui2.convert import Widget
class MetadataWidget(Widget, Ui_Form):
TITLE = _('Metadata')
ICON = ':/images/dialog_information.svg'
HELP = _('Set the metadata. The output file will contain as much of this '
'metadata as possible.')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'metadata', ['prefer_metadata_cover'])
self.db, self.book_id = db, book_id
self.cover_changed = False
if self.db is not None:
self.initialize_metadata_options()
self.initialize_options(get_option, get_help, db, book_id)
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
def initialize_metadata_options(self):
all_series = self.db.all_series()
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
for series in all_series:
self.series.addItem(series[1])
self.series.setCurrentIndex(-1)
mi = self.db.get_metadata(self.book_id, index_is_id=True)
self.title.setText(mi.title)
if mi.authors:
self.author.setText(authors_to_string(mi.authors))
else:
self.author.setText('')
self.publisher.setText(mi.publisher if mi.publisher else '')
self.author_sort.setText(mi.author_sort if mi.author_sort else '')
self.tags.setText(', '.join(mi.tags if mi.tags else []))
self.comment.setText(mi.comments if mi.comments else '')
if mi.series:
self.series.setCurrentIndex(self.series.findText(mi.series))
if mi.series_index is not None:
self.series_index.setValue(mi.series_index)
cover = self.db.cover(self.book_id, index_is_id=True)
if cover:
pm = QPixmap()
pm.loadFromData(cover)
if not pm.isNull():
self.cover.setPixmap(pm)
def get_title_and_authors(self):
title = unicode(self.title.text()).strip()
if not title:
title = _('Unknown')
authors = unicode(self.author.text()).strip()
authors = string_to_authors(authors) if authors else [_('Unknown')]
return title, authors
def get_metadata(self):
title, authors = self.get_title_and_authors()
mi = MetaInformation(title, authors)
publisher = unicode(self.publisher.text()).strip()
if publisher:
mi.publisher = publisher
author_sort = unicode(self.author_sort.text()).strip()
if author_sort:
mi.author_sort = author_sort
comments = unicode(self.comment.toPlainText()).strip()
if comments:
mi.comments = comments
mi.series_index = int(self.series_index.value())
if self.series.currentIndex() > -1:
mi.series = unicode(self.series.currentText()).strip()
tags = [t.strip() for t in unicode(self.tags.text()).strip().split(',')]
if tags:
mi.tags = tags
return mi
def select_cover(self):
files = choose_images(self, 'change cover dialog',
_('Choose cover for ') + unicode(self.title.text()))
if not files:
return
_file = files[0]
if _file:
_file = os.path.abspath(_file)
if not os.access(_file, os.R_OK):
d = error_dialog(self.window, _('Cannot read'),
_('You do not have permission to read the file: ') + _file)
d.exec_()
return
cf, cover = None, None
try:
cf = open(_file, "rb")
cover = cf.read()
except IOError, e:
d = error_dialog(self.window, _('Error reading file'),
_("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e))
d.exec_()
if cover:
pix = QPixmap()
pix.loadFromData(cover)
if pix.isNull():
d = error_dialog(self.window, _('Error reading file'),
_file + _(" is not a valid picture"))
d.exec_()
else:
self.cover_path.setText(_file)
self.cover.setPixmap(pix)
self.cover_changed = True
self.cpixmap = pix
def get_recommendations(self):
return {
'prefer_metadata_cover':
bool(self.opt_prefer_metadata_cover.isChecked()),
}
def commit(self, save_defaults=False):
'''
Settings are stored in two attributes: `opf_file` and `cover_file`.
Both may be None. Also returns a recommendation dictionary.
'''
recs = self.commit_options(save_defaults)
self.user_mi = self.get_metadata()
self.cover_file = self.opf_file = None
if self.db is not None:
self.db.set_metadata(self.book_id, self.user_mi)
self.mi = self.db.get_metadata(self.book_id, index_is_id=True)
self.mi.application_id = uuid.uuid4()
opf = OPFCreator(os.getcwdu(), self.mi)
self.opf_file = PersistentTemporaryFile('.opf')
opf.render(self.opf_file)
self.opf_file.close()
if self.cover_changed:
self.db.set_cover(self.book_id, pixmap_to_data(self.cover.pixmap()))
cover = self.db.cover(self.book_id, index_is_id=True)
if cover:
cf = PersistentTemporaryFile('.jpeg')
cf.write(cover)
cf.close()
self.cover_file = cf
return recs

View File

@ -0,0 +1,336 @@
<ui version="4.0" >
<class>Form</class>
<widget class="QWidget" name="Form" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle" >
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QGroupBox" name="groupBox_4" >
<property name="title" >
<string>Book Cover</string>
</property>
<layout class="QGridLayout" name="_2" >
<item row="1" column="0" >
<layout class="QVBoxLayout" name="_4" >
<property name="spacing" >
<number>6</number>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_5" >
<property name="text" >
<string>Change &amp;cover image:</string>
</property>
<property name="buddy" >
<cstring>cover_path</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="_5" >
<property name="spacing" >
<number>6</number>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="cover_path" >
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="cover_button" >
<property name="toolTip" >
<string>Browse for an image to use as the cover of this book.</string>
</property>
<property name="text" >
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../../../../../../calibre/gui2/images.qrc" >
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="2" column="0" >
<widget class="QCheckBox" name="opt_prefer_metadata_cover" >
<property name="text" >
<string>Use cover from &amp;source file</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" >
<layout class="QHBoxLayout" name="_3" >
<item>
<widget class="ImageView" name="cover" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="../../../../../../calibre/gui2/images.qrc" >:/images/book.svg</pixmap>
</property>
<property name="scaledContents" >
<bool>true</bool>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<zorder>opt_prefer_metadata_cover</zorder>
<zorder></zorder>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2" >
<item>
<layout class="QGridLayout" name="_7" >
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>&amp;Title: </string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>title</cstring>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLineEdit" name="title" >
<property name="toolTip" >
<string>Change the title of this book</string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>&amp;Author(s): </string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>author</cstring>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLineEdit" name="author" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by an &amp;. If the author name contains an &amp;, use &amp;&amp; to represent it.</string>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<string>Author So&amp;rt:</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>author_sort</cstring>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QLineEdit" name="author_sort" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>&amp;Publisher: </string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>publisher</cstring>
</property>
</widget>
</item>
<item row="3" column="1" >
<widget class="QLineEdit" name="publisher" >
<property name="toolTip" >
<string>Change the publisher of this book</string>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>Ta&amp;gs: </string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>tags</cstring>
</property>
</widget>
</item>
<item row="4" column="1" >
<widget class="QLineEdit" name="tags" >
<property name="toolTip" >
<string>Tags categorize the book. This is particularly useful while searching. &lt;br>&lt;br>They can be any words or phrases, separated by commas.</string>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<string>&amp;Series:</string>
</property>
<property name="textFormat" >
<enum>Qt::PlainText</enum>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>series</cstring>
</property>
</widget>
</item>
<item row="5" column="1" >
<widget class="QComboBox" name="series" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
<horstretch>10</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip" >
<string>List of known series. You can add new series.</string>
</property>
<property name="whatsThis" >
<string>List of known series. You can add new series.</string>
</property>
<property name="editable" >
<bool>true</bool>
</property>
<property name="insertPolicy" >
<enum>QComboBox::InsertAlphabetically</enum>
</property>
<property name="sizeAdjustPolicy" >
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="6" column="1" >
<widget class="QSpinBox" name="series_index" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="toolTip" >
<string>Series index.</string>
</property>
<property name="whatsThis" >
<string>Series index.</string>
</property>
<property name="prefix" >
<string>Book </string>
</property>
<property name="minimum" >
<number>1</number>
</property>
<property name="maximum" >
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize" >
<size>
<width>16777215</width>
<height>200</height>
</size>
</property>
<property name="title" >
<string>Comments</string>
</property>
<layout class="QGridLayout" name="_8" >
<item row="0" column="0" >
<widget class="QTextEdit" name="comment" >
<property name="maximumSize" >
<size>
<width>16777215</width>
<height>180</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ImageView</class>
<extends>QLabel</extends>
<header>widgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../../../../calibre/gui2/images.qrc" />
<include location="../images.qrc" />
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.convert.mobi_output_ui import Ui_Form
from calibre.gui2.convert import Widget
class PluginWidget(Widget, Ui_Form):
TITLE = _('MOBI Output')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'mobi_output',
['prefer_author_sort', 'rescale_images', 'toc_title']
)
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Title for Table of Contents:</string>
</property>
<property name="buddy">
<cstring>opt_toc_title</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="opt_toc_title"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="opt_rescale_images">
<property name="text">
<string>Rescale images for &amp;Palm devices</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="opt_prefer_author_sort">
<property name="text">
<string>Use author &amp;sort for author</string>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,74 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import Qt, QAbstractListModel, QVariant, SIGNAL
from calibre.gui2.convert.page_setup_ui import Ui_Form
from calibre.gui2.convert import Widget
from calibre.gui2 import NONE
from calibre.customize.ui import input_profiles, output_profiles
class ProfileModel(QAbstractListModel):
def __init__(self, profiles):
QAbstractListModel.__init__(self)
self.profiles = list(profiles)
def rowCount(self, *args):
return len(self.profiles)
def data(self, index, role):
profile = self.profiles[index.row()]
if role == Qt.DisplayRole:
return QVariant(profile.name)
if role in (Qt.ToolTipRole, Qt.StatusTipRole, Qt.WhatsThisRole):
return QVariant(profile.description)
return NONE
class PageSetupWidget(Widget, Ui_Form):
TITLE = _('Page Setup')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'lrf_output',
['margin_top', 'margin_left', 'margin_right', 'margin_bottom',
'input_profile', 'output_profile']
)
self.db, self.book_id = db, book_id
self.input_model = ProfileModel(input_profiles())
self.output_model = ProfileModel(output_profiles())
self.opt_input_profile.setModel(self.input_model)
self.opt_output_profile.setModel(self.output_model)
for x in (self.opt_input_profile, self.opt_output_profile):
x.setMouseTracking(True)
self.connect(x, SIGNAL('entered(QModelIndex)'), self.show_desc)
self.initialize_options(get_option, get_help, db, book_id)
def show_desc(self, index):
desc = index.model().data(index, Qt.StatusTipRole).toString()
self.profile_description.setText(desc)
def set_value_handler(self, g, val):
if g in (self.opt_input_profile, self.opt_output_profile):
g.clearSelection()
for idx, p in enumerate(g.model().profiles):
if p.short_name == val:
break
idx = g.model().index(idx)
sm = g.selectionModel()
g.setCurrentIndex(idx)
sm.select(idx, sm.SelectCurrent)
return True
return False
def get_value_handler(self, g):
if g in (self.opt_input_profile, self.opt_output_profile):
idx = g.currentIndex().row()
return g.model().profiles[idx].short_name
return Widget.get_value_handler(self, g)

View File

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>572</width>
<height>476</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Output profile:</string>
</property>
<property name="buddy">
<cstring>opt_output_profile</cstring>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="opt_output_profile"/>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>4</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Profile description</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="profile_description">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>4</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Input profile:</string>
</property>
<property name="buddy">
<cstring>opt_input_profile</cstring>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="opt_input_profile"/>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>4</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Margins</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Left:</string>
</property>
<property name="buddy">
<cstring>opt_margin_left</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="opt_margin_left">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>&amp;Top:</string>
</property>
<property name="buddy">
<cstring>opt_margin_top</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="opt_margin_top">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>&amp;Right:</string>
</property>
<property name="buddy">
<cstring>opt_margin_right</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="opt_margin_right">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Bottom:</string>
</property>
<property name="buddy">
<cstring>opt_margin_bottom</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="opt_margin_bottom">
<property name="suffix">
<string> pt</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,244 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys, cPickle
from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont
from calibre.gui2 import ResizableDialog, NONE
from calibre.gui2.convert import GuiRecommendations, save_specifics, \
load_specifics
from calibre.gui2.convert.single_ui import Ui_Dialog
from calibre.gui2.convert.metadata import MetadataWidget
from calibre.gui2.convert.look_and_feel import LookAndFeelWidget
from calibre.gui2.convert.page_setup import PageSetupWidget
from calibre.ebooks.conversion.plumber import Plumber, supported_input_formats, \
INPUT_FORMAT_PREFERENCES, OUTPUT_FORMAT_PREFERENCES
from calibre.customize.ui import available_output_formats
from calibre.customize.conversion import OptionRecommendation
from calibre.utils.logging import Log
class NoSupportedInputFormats(Exception):
pass
def sort_formats_by_preference(formats, prefs):
def fcmp(x, y):
try:
x = prefs.index(x)
except ValueError:
x = sys.maxint
try:
y = prefs.index(y)
except ValueError:
y = sys.maxint
return cmp(x, y)
return sorted(formats, cmp=fcmp)
class GroupModel(QAbstractListModel):
def __init__(self, widgets):
self.widgets = widgets
QAbstractListModel.__init__(self)
def rowCount(self, *args):
return len(self.widgets)
def data(self, index, role):
try:
widget = self.widgets[index.row()]
except:
return NONE
if role == Qt.DisplayRole:
return QVariant(widget.config_title())
if role == Qt.DecorationRole:
return QVariant(widget.config_icon())
if role == Qt.FontRole:
f = QFont()
f.setBold(True)
return QVariant(f)
return NONE
class Config(ResizableDialog, Ui_Dialog):
'''
Configuration dialog for single book conversion. If accepted, has the
following important attributes
input_path - Path to input file
output_format - Output format (without a leading .)
input_format - Input format (without a leading .)
opf_path - Path to OPF file with user specified metadata
cover_path - Path to user specified cover (can be None)
recommendations - A pickled list of 3 tuples in the same format as the
recommendations member of the Input/Output plugins.
'''
def __init__(self, parent, db, book_id,
preferred_input_format=None, preferred_output_format=None):
ResizableDialog.__init__(self, parent)
if preferred_input_format is None and db is not None:
recs = load_specifics(db, book_id)
if recs:
preferred_input_format = recs.get('gui_preferred_input_format',
None)
self.setup_input_output_formats(db, book_id, preferred_input_format,
preferred_input_format)
self.db, self.book_id = db, book_id
self.setup_pipeline()
self.connect(self.input_formats, SIGNAL('currentIndexChanged(QString)'),
self.setup_pipeline)
self.connect(self.output_formats, SIGNAL('currentIndexChanged(QString)'),
self.setup_pipeline)
self.connect(self.groups, SIGNAL('activated(QModelIndex)'),
self.show_pane)
self.connect(self.groups, SIGNAL('clicked(QModelIndex)'),
self.show_pane)
self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
self.show_group_help)
self.groups.setMouseTracking(True)
@property
def input_format(self):
return unicode(self.input_formats.currentText()).lower()
@property
def output_format(self):
return unicode(self.output_formats.currentText()).lower()
def setup_pipeline(self, *args):
input_format = self.input_format
output_format = self.output_format
input_path = self.db.format_abspath(self.book_id, input_format,
index_is_id=True)
self.input_path = input_path
output_path = 'dummy.'+output_format
log = Log()
log.outputs = []
self.plumber = Plumber(input_path, output_path, log)
def widget_factory(cls):
return cls(self.stack, self.plumber.get_option_by_name,
self.plumber.get_option_help, self.db, self.book_id)
self.mw = widget_factory(MetadataWidget)
self.setWindowTitle(_('Convert')+ ' ' + unicode(self.mw.title.text()))
lf = widget_factory(LookAndFeelWidget)
ps = widget_factory(PageSetupWidget)
output_widget = None
name = self.plumber.output_plugin.name.lower().replace(' ', '_')
try:
output_widget = __import__(name)
pw = output_widget.PluginWidget
pw.ICON = ':/images/back.svg'
pw.HELP = _('Options specific to the output format.')
output_widget = widget_factory(pw)
except ImportError:
pass
input_widget = None
name = self.plumber.input_plugin.name.lower().replace(' ', '_')
try:
input_widget = __import__(name)
pw = input_widget.PluginWidget
pw.ICON = ':/images/forward.svg'
pw.HELP = _('Options specific to the input format.')
input_widget = widget_factory(pw)
except ImportError:
pass
while True:
c = self.stack.currentWidget()
if not c: break
self.stack.removeWidget(c)
widgets = [self.mw, lf, ps]
if input_widget is not None:
widgets.append(input_widget)
if output_widget is not None:
widgets.append(output_widget)
for w in widgets:
self.stack.addWidget(w)
self.connect(w, SIGNAL('set_help(PyQt_PyObject)'),
self.help.setPlainText)
self._groups_model = GroupModel(widgets)
self.groups.setModel(self._groups_model)
self.groups.setCurrentIndex(self._groups_model.index(0))
def setup_input_output_formats(self, db, book_id, preferred_input_format,
preferred_output_format):
available_formats = db.formats(book_id, index_is_id=True)
if not available_formats:
available_formats = ''
available_formats = set([x.lower() for x in
available_formats.split(',')])
input_formats = set([x.lower() for x in supported_input_formats()])
input_formats = \
sorted(available_formats.intersection(input_formats))
if not input_formats:
raise NoSupportedInputFormats
output_formats = sorted(available_output_formats())
output_formats.remove('oeb')
preferred_input_format = preferred_input_format if \
preferred_input_format in input_formats else \
sort_formats_by_preference(input_formats,
INPUT_FORMAT_PREFERENCES)[0]
preferred_output_format = preferred_output_format if \
preferred_output_format in output_formats else \
sort_formats_by_preference(output_formats,
OUTPUT_FORMAT_PREFERENCES)[0]
self.input_formats.addItems(list(map(QString, [x.upper() for x in
input_formats])))
self.output_formats.addItems(list(map(QString, [x.upper() for x in
output_formats])))
self.input_formats.setCurrentIndex(input_formats.index(preferred_input_format))
self.output_formats.setCurrentIndex(output_formats.index(preferred_output_format))
def show_pane(self, index):
self.stack.setCurrentIndex(index.row())
def accept(self):
recs = GuiRecommendations()
for w in self._groups_model.widgets:
x = w.commit(save_defaults=False)
recs.update(x)
self.opf_path, self.cover_path = self.mw.opf_file, self.mw.cover_file
self._recommendations = recs
if self.db is not None:
recs['gui_preferred_input_format'] = self.input_format
save_specifics(self.db, self.book_id, recs)
ResizableDialog.accept(self)
@property
def recommendations(self):
recs = [(k, v, OptionRecommendation.HIGH) for k, v in
self._recommendations.items()]
return cPickle.dumps(recs, -1)
def show_group_help(self, index):
widget = self._groups_model.widgets[index.row()]
self.help.setPlainText(widget.HELP)
if __name__ == '__main__':
from calibre.library.database2 import LibraryDatabase2
from calibre.gui2 import images_rc, Application
images_rc
a = Application([])
db = LibraryDatabase2('/home/kovid/documents/library')
d = Config(None, db, 594)
d.show()
a.exec_()

View File

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>700</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="windowIcon">
<iconset resource="../images.qrc">
<normaloff>:/images/convert.svg</normaloff>:/images/convert.svg</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Input format:</string>
</property>
<property name="buddy">
<cstring>input_formats</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="input_formats"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Output format:</string>
</property>
<property name="buddy">
<cstring>output_formats</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="output_formats"/>
</item>
</layout>
</item>
<item row="1" column="0" rowspan="3">
<widget class="QListView" name="groups">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="tabKeyNavigation">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="spacing">
<number>20</number>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>4</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>810</width>
<height>492</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QStackedWidget" name="stack">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="3" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QTextEdit" name="help">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>130</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -285,7 +285,7 @@ class FontFamilyModel(QAbstractListModel):
print 'WARNING: Could not load fonts' print 'WARNING: Could not load fonts'
traceback.print_exc() traceback.print_exc()
self.families.sort() self.families.sort()
self.families[:0] = ['None'] self.families[:0] = [_('None')]
def rowCount(self, *args): def rowCount(self, *args):
return len(self.families) return len(self.families)

View File

@ -53,7 +53,7 @@ def get_opts_from_parser(parser, prefix):
for x in do_opt(o): yield x for x in do_opt(o): yield x
def send(ans): def send(ans):
pat = re.compile('([^0-9a-zA-Z_.])') pat = re.compile('([^0-9a-zA-Z_./])')
for x in sorted(set(ans)): for x in sorted(set(ans)):
x = pat.sub(lambda m : '\\'+m.group(1), x) x = pat.sub(lambda m : '\\'+m.group(1), x)
if x.endswith('\\ '): if x.endswith('\\ '):

4
todo
View File

@ -3,14 +3,12 @@
* Refactor IPC code to use communication logic from multiprocessing * Refactor IPC code to use communication logic from multiprocessing
* Use multiprocessing for cpu_count instead of QThread
* Rationalize books table. Add a pubdate column, remove the uri column (and associated support in add_books) and convert series_index to a float. * Rationalize books table. Add a pubdate column, remove the uri column (and associated support in add_books) and convert series_index to a float.
* Replace single application stuff with Listener from multiprocessing * Replace single application stuff with Listener from multiprocessing
* Refactor add books to use a separate process named calibre-worker-add * Refactor add books to use a separate process named calibre-worker-add
- Dont use the process for adding a single book - Dont use the process for adding a single book
- Use a process pool for speed - Use a process pool for speed or multiple process for stability (20 per process?)
* Change mobi metadata setter to use author_sort setting from MOBI output plugin instead of mobi.py * Change mobi metadata setter to use author_sort setting from MOBI output plugin instead of mobi.py

View File

@ -286,7 +286,7 @@ class gui(OptionlessCommand):
with open('images.qrc', 'wb') as f: with open('images.qrc', 'wb') as f:
f.write(manifest) f.write(manifest)
try: try:
check_call(['pyrcc4', '-o', images, 'images.qrc']) check_call(['pyrcc4', '-py2', '-o', images, 'images.qrc'])
except: except:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
@ -399,7 +399,7 @@ class update(OptionlessCommand):
' a version update.' ' a version update.'
def run(self): def run(self):
for x in ['build', 'dist', 'docs'] + \ for x in ['build', 'dist'] + \
glob.glob(os.path.join('src', 'calibre', 'plugins', '*')): glob.glob(os.path.join('src', 'calibre', 'plugins', '*')):
if os.path.exists(x): if os.path.exists(x):
if os.path.isdir(x): if os.path.isdir(x):