Merge version 0.7.12

This commit is contained in:
Sengian 2010-07-31 10:49:01 +02:00
commit a80c304fa4
49 changed files with 17557 additions and 12110 deletions

View File

@ -4,6 +4,78 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
- version: 0.7.12
date: 2010-07-30
bug fixes:
- title: "Fix a typo that caused a harmless error message after setting preferences"
- title: "Linux build system: No longer search for poppler-qt4 libraries as they are not needed"
- version: 0.7.11
date: 2010-07-30
new features:
- title: "EPUB metadata: When setting metadata in an EPUB file, if it has a well defined image based cover, update it"
- title: "Support for Droid X, Samsung Vibrant and Promedia ebook reader"
- title: "Add entry to Connect/share menu to conveniently stop and start the Content Server"
- title: "News download: Make the navbars on the section index pages more useful, adding links to net and previous section"
- title: "Add a button to reset confirm dialogs to Preferences->General"
bug fixes:
- title: "Fix crash in edit metadata dialog if you click OK before cover download completes"
tickets: [6337]
- title: "Kobo driver: Show a warning when the user tries to export/view .kobo files. Also add support for the new sofroot vendor id"
- title: "Update check. Do not be fooled by a redirecting proxy when checking for new version"
tickets: [6325]
- title: "Add book count to tooltip of library button in toolbar"
tickets: [6340]
- title: "Content server: When serving OPDS feeds send the correct content-type header."
tickets: [6329]
- title: "PDF Output: Don't insert blank pages before every individual HTML file in the ebook."
tickets: [6315]
- title: "Fix saving of cover when path to book folder contains non ascii characters"
tickets: [6328]
- title: "Fix regression that broke showing send to actions for multiple email accounts"
- title: "Fix bug in handlling of hexadecimal entities"
tickets: [6305]
- title: "SONY driver: More fixes to handle broken media.xml files"
tickets: [6296]
- title: "Linux installer: Fix rendering of viewer icon and restrict all icons to 128x128 since GNOME can't handle large icons"
- title: "RTF Input: Fix handling of hard linebreaks"
tickets: [6208]
- title: "RTF Output: Fix regression that broke rendering of bold and italic text"
tickets: [6098]
new recipes:
- title: "Draw and Cook"
author: Startson17
improved recipes:
- La Nacion
- Vecernje Novosti
- Der Tagesspiegel
- Die Zeit Nachrichten
- Toms Hardware (DE)
- Welt Online
- version: 0.7.10
date: 2010-07-23

View File

@ -0,0 +1,60 @@
from calibre.web.feeds.news import BasicNewsRecipe
class DrawAndCook(BasicNewsRecipe):
title = 'DrawAndCook'
__author__ = 'Starson17'
description = 'Drawings of recipes!'
language = 'en'
publisher = 'Starson17'
category = 'news, food, recipes'
use_embedded_content= False
no_stylesheets = True
oldest_article = 24
remove_javascript = True
remove_empty_feeds = True
cover_url = 'http://farm5.static.flickr.com/4043/4471139063_4dafced67f_o.jpg'
max_articles_per_feed = 30
remove_attributes = ['style', 'font']
def parse_index(self):
feeds = []
for title, url in [
("They Draw and Cook", "http://www.theydrawandcook.com/")
]:
articles = self.make_links(url)
if articles:
feeds.append((title, articles))
print 'feeds are: ', feeds
return feeds
def make_links(self, url):
soup = self.index_to_soup(url)
title = ''
date = ''
current_articles = []
soup = self.index_to_soup(url)
recipes = soup.findAll('div', attrs={'class': 'date-outer'})
for recipe in recipes:
title = recipe.h3.a.string
page_url = recipe.h3.a['href']
current_articles.append({'title': title, 'url': page_url, 'description':'', 'date':date})
return current_articles
keep_only_tags = [dict(name='h3', attrs={'class':'post-title entry-title'})
,dict(name='div', attrs={'class':'post-body entry-content'})
]
remove_tags = [dict(name='div', attrs={'class':['separator']})
,dict(name='div', attrs={'class':['post-share-buttons']})
]
extra_css = '''
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
img {max-width:100%; min-width:100%;}
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
'''

View File

@ -98,7 +98,7 @@ class ZeitDe(BasicNewsRecipe):
return soup
info = Tag(soup,'ul',[('class','ebinfobox')])
tools = soup.find('ul', attrs={'class':'tools'})
author = tools.find('li','author first')
#author = tools.find('li','author first')
for tag in ['author first', 'date', 'date first', 'author', 'source']:
line = tools.find('li', tag)
if line:

View File

@ -115,7 +115,6 @@ if iswindows:
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR', sw_lib_dir)
popplerqt4_lib_dirs = poppler_lib_dirs
poppler_libs = ['poppler']
popplerqt4_libs = poppler_libs + ['QtCore4', 'QtGui4']
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.5.6')]
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
magick_libs = ['CORE_RL_wand_', 'CORE_RL_magick_']
@ -129,8 +128,8 @@ elif isosx:
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
'/sw/lib')
poppler_libs = ['poppler']
popplerqt4_lib_dirs = poppler_lib_dirs
poppler_libs = popplerqt4_libs = ['poppler']
podofo_inc = '/sw/podofo'
podofo_lib = '/sw/lib'
magick_inc_dirs = consolidate('MAGICK_INC',
@ -162,9 +161,6 @@ else:
poppler_libs = pkgconfig_libs('poppler', '', '')
if not poppler_libs:
poppler_libs = ['poppler']
popplerqt4_libs = pkgconfig_libs('poppler-qt4', '', '')
if not popplerqt4_libs:
popplerqt4_libs = ['poppler-qt4', 'poppler']
magick_libs = pkgconfig_libs('MagickWand', '', '')
if not magick_libs:
magick_libs = ['MagickWand', 'MagickCore']

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.10'
__version__ = '0.7.12'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -22,7 +22,8 @@ class ANDROID(USBMS):
0x0bb4 : { 0x0c02 : [0x100], 0x0c01 : [0x100], 0x0ff9 : [0x0100]},
# Motorola
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216]},
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
0x4285 : [0x216]},
# Sony Ericsson
0xfce : { 0xd12e : [0x0100]},
@ -52,9 +53,9 @@ class ANDROID(USBMS):
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
'GT-I9000', 'FILE-STOR_GADGET']
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
'FILE-STOR_GADGET']
'FILE-STOR_GADGET', 'SGH-T959']
OSX_MAIN_MEM = 'HTC Android Phone Media'

View File

@ -355,3 +355,17 @@ class KOBO(USBMS):
# print "Internal: " + filename
return path
def get_file(self, path, *args, **kwargs):
tpath = self.munge_path(path)
extension = os.path.splitext(tpath)[1]
if extension == '.kobo':
from calibre.devices.errors import UserFeedback
raise UserFeedback(_("Not Implemented"),
_('".kobo" files do not exist on the device as books '
'instead, they are rows in the sqlite database. '
'Currently they cannot be exported or viewed.'),
UserFeedback.WARN)
return USBMS.get_file(self, path, *args, **kwargs)

View File

@ -82,7 +82,7 @@ class Metadata(object):
def print_all_attributes(self):
pass
def smart_update(self, other):
def smart_update(self, other, replace_metadata=False):
pass
def format_series_index(self):

View File

@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''Read meta information from epub files'''
import os, re
import os, re, posixpath
from cStringIO import StringIO
from contextlib import closing
@ -126,7 +126,6 @@ class OCFDirReader(OCFReader):
return open(os.path.join(self.root, path), *args, **kwargs)
def get_cover(opf, opf_path, stream, reader=None):
import posixpath
from calibre.ebooks import render_html_svg_workaround
from calibre.utils.logging import default_log
raster_cover = opf.raster_cover
@ -185,7 +184,37 @@ def get_quick_metadata(stream):
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
stream.seek(0)
reader = OCFZipReader(stream, root=os.getcwdu())
raster_cover = reader.opf.raster_cover
mi = MetaInformation(mi)
new_cdata = None
replacements = {}
try:
new_cdata = mi.cover_data[1]
if not new_cdata:
raise Exception('no cover')
except:
try:
new_cdata = open(mi.cover, 'rb').read()
except:
import traceback
traceback.print_exc()
if new_cdata and raster_cover:
try:
cpath = posixpath.join(posixpath.dirname(reader.opf_path),
raster_cover)
cover_replacable = not reader.encryption_meta.is_encrypted(cpath) and \
os.path.splitext(cpath)[1].lower() in ('.png', '.jpg', '.jpeg')
if cover_replacable:
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.magick_draw import save_cover_data_to
new_cover = PersistentTemporaryFile(suffix=os.path.splitext(cpath)[1])
new_cover.close()
save_cover_data_to(new_cdata, new_cover.name)
replacements[cpath] = open(new_cover.name, 'rb')
except:
import traceback
traceback.print_exc()
for x in ('guide', 'toc', 'manifest', 'spine'):
setattr(mi, x, None)
reader.opf.smart_update(mi)
@ -200,5 +229,6 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
reader.opf.timestamp = mi.timestamp
newopf = StringIO(reader.opf.render())
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf)
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf,
extra_replacements=replacements)

View File

@ -916,7 +916,7 @@ class OPF(object):
raw = '<?xml version="1.0" encoding="%s"?>\n'%encoding.upper()+raw
return raw
def smart_update(self, mi):
def smart_update(self, mi, replace_metadata=False):
for attr in ('title', 'authors', 'author_sort', 'title_sort',
'publisher', 'series', 'series_index', 'rating',
'isbn', 'language', 'tags', 'category', 'comments',

View File

@ -59,6 +59,29 @@ class PDFOutput(OutputFormatPlugin):
self.metadata = oeb_book.metadata
self.cover_data = None
# Remove page-break-before on <body> element as it causes
# blank pages in PDF Output
from calibre.ebooks.oeb.base import OEB_STYLES, XPath
stylesheet = None
for item in self.oeb.manifest:
if item.media_type.lower() in OEB_STYLES:
stylesheet = item
break
if stylesheet is not None:
from cssutils.css import CSSRule
classes = set(['.calibre'])
for x in self.oeb.spine:
root = x.data
body = XPath('//h:body[@class]')(root)
if body:
classes.add('.'+body[0].get('class'))
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
if rule.selectorList.selectorText in classes:
rule.style.removeProperty('page-break-before')
rule.style.removeProperty('page-break-after')
if input_plugin.is_image_collection:
log.debug('Converting input as an image collection...')
self.convert_images(input_plugin.get_images())

View File

@ -677,6 +677,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
def accept(self):
cf = getattr(self, 'cover_fetcher', None)
if cf is not None and hasattr(cf, 'terminate'):
cf.terminate()
cf.wait()
try:
if self.formats_changed:
self.sync_formats()

View File

@ -302,6 +302,7 @@ class ToolBar(QToolBar): # {{{
text = _('%d books')%new_count
a = self.choose_action
a.setText(text)
a.setToolTip(_('Choose calibre library to work with') + '\n\n' + text)
def resizeEvent(self, ev):
QToolBar.resizeEvent(self, ev)
@ -328,6 +329,7 @@ class ShareConnMenu(QMenu): # {{{
connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal()
config_email = pyqtSignal()
toggle_server = pyqtSignal()
def __init__(self, parent=None):
QMenu.__init__(self, parent)
@ -335,15 +337,27 @@ class ShareConnMenu(QMenu): # {{{
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_folder.emit())
self.connect_to_folder_action = mitem
mitem = self.addAction(QIcon(I('devices/itunes.png')),
_('Connect to iTunes'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
self.connect_to_itunes_action = mitem
self.addSeparator()
self.toggle_server_action = \
self.addAction(QIcon(I('network-server.svg')),
_('Start Content Server'))
self.toggle_server_action.triggered.connect(lambda x:
self.toggle_server.emit())
self.addSeparator()
self.email_actions = []
def server_state_changed(self, running):
text = _('Start Content Server')
if running:
text = _('Stop Content Server')
self.toggle_server_action.setText(text)
def build_email_entries(self, sync_menu):
from calibre.gui2.device import DeviceAction
for ac in self.email_actions:
@ -477,6 +491,7 @@ class MainWindowMixin(object):
self.action_news.triggered.connect(
self.scheduler.show_dialog)
self.share_conn_menu = ShareConnMenu(self)
self.share_conn_menu.toggle_server.connect(self.toggle_content_server)
self.share_conn_menu.config_email.connect(partial(self.do_config,
initial_category='email'))
self.action_conn_share.setMenu(self.share_conn_menu)
@ -613,4 +628,12 @@ class MainWindowMixin(object):
def show_help(self, *args):
open_url(QUrl('http://calibre-ebook.com/user_manual'))
def content_server_state_changed(self, running):
self.share_conn_menu.server_state_changed(running)
def toggle_content_server(self):
if self.content_server is None:
self.start_content_server()
else:
self.content_server.exit()
self.content_server = None

View File

@ -221,7 +221,7 @@ def fetch_scheduled_recipe(arg):
if lf.get('base_font_size', 0.0) != 0.0:
recs.append(('base_font_size', lf['base_font_size'],
OptionRecommendation.HIGH))
recs.append(('keep_ligatures', lf['keep_ligatures'],
recs.append(('keep_ligatures', lf.get('keep_ligatures', False),
OptionRecommendation.HIGH))
lr = load_defaults('lrf_output')

View File

@ -24,7 +24,7 @@ from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import prefs, dynamic
from calibre.utils.ipc.server import Server
from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \
gprefs, max_available_height, config, info_dialog
gprefs, max_available_height, config, info_dialog, Dispatcher
from calibre.gui2.cover_flow import CoverFlowMixin
from calibre.gui2.widgets import ProgressIndicator
from calibre.gui2.update import UpdateMixin
@ -106,6 +106,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
opts = self.opts
self.preferences_action, self.quit_action = actions
self.library_path = library_path
self.content_server = None
self.spare_servers = []
self.must_restart_before_config = False
# Initialize fontconfig in a separate thread as this can be a lengthy
@ -146,7 +147,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
self.default_thumbnail = None
self.tb_wrapper = textwrap.TextWrapper(width=40)
self.viewers = collections.deque()
self.content_server = None
self.system_tray_icon = SystemTrayIcon(QIcon(I('library.png')), self)
self.system_tray_icon.setToolTip('calibre')
self.system_tray_icon.tooltip_requested.connect(
@ -246,11 +246,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
if config['autolaunch_server']:
from calibre.library.server.main import start_threaded_server
from calibre.library.server import server_config
self.content_server = start_threaded_server(
db, server_config().parse())
self.test_server_timer = QTimer.singleShot(10000, self.test_server)
self.start_content_server()
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
AddAction.__init__(self)
@ -263,6 +259,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
self.library_view.model().delete_books_by_id,
type=Qt.QueuedConnection)
def start_content_server(self):
from calibre.library.server.main import start_threaded_server
from calibre.library.server import server_config
self.content_server = start_threaded_server(
self.library_view.model().db, server_config().parse())
self.content_server.state_callback = Dispatcher(self.content_server_state_changed)
self.content_server.state_callback(True)
self.test_server_timer = QTimer.singleShot(10000, self.test_server)
def resizeEvent(self, ev):
MainWindow.resizeEvent(self, ev)
@ -308,7 +313,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
setattr(window, '__systray_minimized', False)
def test_server(self, *args):
if self.content_server.exception is not None:
if self.content_server is not None and \
self.content_server.exception is not None:
error_dialog(self, _('Failed to start content server'),
unicode(self.content_server.exception)).exec_()
@ -367,6 +373,11 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
d.exec_()
self.content_server = d.server
if self.content_server is not None:
self.content_server.state_callback = \
Dispatcher(self.content_server_state_changed)
self.content_server.state_callback(self.content_server.is_running)
if d.result() == d.Accepted:
self.read_toolbar_settings()
self.search.search_as_you_type(config['search_as_you_type'])
@ -583,7 +594,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
try:
try:
if self.content_server is not None:
self.content_server.exit()
s = self.content_server
self.content_server = None
s.exit()
except:
pass
time.sleep(2)

View File

@ -31,7 +31,7 @@ class CheckForUpdates(QThread):
'win' if iswindows else 'osx' if isosx else 'oth')
req.add_header('CALIBRE_INSTALL_UUID', prefs['installation_uuid'])
version = br.open(req).read().strip()
if version and version != __version__:
if version and version != __version__ and len(version) < 10:
self.update_found.emit(version)
except:
traceback.print_exc()

View File

@ -64,6 +64,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache):
break
self.opts = opts
self.embedded = embedded
self.state_callback = None
self.max_cover_width, self.max_cover_height = \
map(int, self.opts.max_cover.split('x'))
path = P('content_server')
@ -159,11 +160,22 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache):
import traceback
cherrypy.log.error('Failed to stop BonJour:')
cherrypy.log.error(traceback.format_exc())
try:
if callable(self.state_callback):
self.state_callback(self.is_running)
except:
pass
def exit(self):
try:
cherrypy.engine.exit()
finally:
cherrypy.server.httpserver = None
self.is_running = False
try:
if callable(self.state_callback):
self.state_callback(self.is_running)
except:
pass

View File

@ -183,7 +183,8 @@ class ContentServer(object):
fmt = TemporaryFile()
fmt.write(raw)
fmt.seek(0)
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True),
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True,
get_cover=True),
'epub')
fmt.seek(0)
mt = guess_type('dummy.'+format.lower())[0]

View File

@ -341,7 +341,7 @@ class OPDSServer(object):
items = items[offsets.offset:offsets.offset+max_items]
updated = self.db.last_modified()
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
cherrypy.response.headers['Content-Type'] = 'text/xml'
cherrypy.response.headers['Content-Type'] = 'application/atom+xml;profile=opds-catalog'
return str(AcquisitionFeed(updated, id_, items, offsets,
page_url, up_url, version, self.db.FIELD_MAP))
@ -413,7 +413,7 @@ class OPDSServer(object):
items = list(items)[offsets.offset:offsets.offset+max_items]
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
cherrypy.response.headers['Content-Type'] = 'text/xml'
cherrypy.response.headers['Content-Type'] = 'application/atom+xml'
return str(CategoryFeed(items, category, id_, updated, version, offsets,
page_url, up_url))
@ -478,7 +478,7 @@ class OPDSServer(object):
page_url, up_url)
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
cherrypy.response.headers['Content-Type'] = 'text/xml'
cherrypy.response.headers['Content-Type'] = 'application/atom+xml'
return str(ans)
@ -552,7 +552,7 @@ class OPDSServer(object):
updated = self.db.last_modified()
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
cherrypy.response.headers['Content-Type'] = 'text/xml'
cherrypy.response.headers['Content-Type'] = 'application/atom+xml'
feed = TopLevel(updated, cats, version)

View File

@ -376,6 +376,12 @@ How do I use purchased EPUB books with |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
Can I have the comment metadata show up on my reader?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most readers do not support this. You should complain to the manufacturer about it and hopefully if enough people complain, things will change. In the meantime, you can insert the metadata, including comments into a "Jacket page" at the start of the ebook, by using the option to "Insert metadata as page at start of book" during conversion. The option is found in the :guilabel:`Structure Detection` section of the conversion settings. Note that for this to have effect you have to *convert* the book. If your book is already in a format that does not need conversion, you can convert from that format to the same format.
Another alternative is to create a catalog in ebook form containing a listing of all the books in your calibre library, with their metadata. Click the arrow next to the convert button to access the catalog creation tool. And before you ask, no you cannot have the catalog "link directly to" books on your reader.
I want some feature added to |app|. What can I do?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -144,6 +144,8 @@ def compose_image(canvas, image, left, top):
int(top))
def load_image(path):
if isinstance(path, unicode):
path = path.encode(filesystem_encoding)
img = alloc_wand('NewMagickWand')
if not p.MagickReadImage(img, path):
severity = p.ExceptionType(0)
@ -221,7 +223,7 @@ def create_cover_page(top_lines, logo_path, width=590, height=750,
def save_cover_data_to(data, path, bgcolor='white'):
'''
Saves image in data to path, in the format specified by the path
extension. Composes the image onto a blank cancas so as to
extension. Composes the image onto a blank canvas so as to
properly convert transparent images.
'''
with open(path, 'wb') as f:

View File

@ -1362,7 +1362,7 @@ class ZipFile:
self.fp.close()
self.fp = None
def safe_replace(zipstream, name, datastream):
def safe_replace(zipstream, name, datastream, extra_replacements={}):
'''
Replace a file in a zip file in a safe manner. This proceeds by extracting
and re-creating the zipfile. This is necessary because :method:`ZipFile.replace`
@ -1371,13 +1371,19 @@ def safe_replace(zipstream, name, datastream):
:param zipstream: Stream from a zip file
:param name: The name of the file to replace
:param datastream: The data to replace the file with.
:param extra_replacements: Extra replacements. Mapping of name to file-like
objects
'''
z = ZipFile(zipstream, 'r')
replacements = {name:datastream}
replacements.update(extra_replacements)
names = frozenset(replacements.keys())
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
ztemp = ZipFile(temp, 'w')
for obj in z.infolist():
if obj.filename == name:
ztemp.writestr(obj, datastream.read())
if obj.filename in names:
ztemp.writestr(obj, replacements[obj.filename].read())
else:
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
ztemp.close()