mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Regex builder: Show a nicer error message when the user has the file open in another program on winblows. Fixes #811641 (Convert books fails)
This commit is contained in:
parent
2b379f7ab8
commit
830b0b5a10
118
src/calibre/ebooks/mobi/writer2/utils.py
Normal file
118
src/calibre/ebooks/mobi/writer2/utils.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from calibre.utils.magick.draw import Image, save_cover_data_to, thumbnail
|
||||||
|
|
||||||
|
DECINT_FORWARD = 0
|
||||||
|
DECINT_BACKWARD = 1
|
||||||
|
IMAGE_MAX_SIZE = 10 * 1024 * 1024
|
||||||
|
|
||||||
|
def decode_hex_number(raw):
|
||||||
|
'''
|
||||||
|
Return a variable length number encoded using hexadecimal encoding. These
|
||||||
|
numbers have the first byte which tells the number of bytes that follow.
|
||||||
|
The bytes that follow are simply the hexadecimal representation of the
|
||||||
|
number.
|
||||||
|
|
||||||
|
:param raw: Raw binary data as a bytestring
|
||||||
|
|
||||||
|
:return: The number and the number of bytes from raw that the number
|
||||||
|
occupies
|
||||||
|
'''
|
||||||
|
length, = struct.unpack(b'>B', raw[0])
|
||||||
|
raw = raw[1:1+length]
|
||||||
|
consumed = length+1
|
||||||
|
return int(raw, 16), consumed
|
||||||
|
|
||||||
|
def encode_number_as_hex(num):
|
||||||
|
'''
|
||||||
|
Encode num as a variable length encoded hexadecimal number. Returns the
|
||||||
|
bytestring containing the encoded number. These
|
||||||
|
numbers have the first byte which tells the number of bytes that follow.
|
||||||
|
The bytes that follow are simply the hexadecimal representation of the
|
||||||
|
number.
|
||||||
|
'''
|
||||||
|
num = bytes(hex(num)[2:])
|
||||||
|
ans = bytearray(num)
|
||||||
|
ans.insert(0, len(num))
|
||||||
|
return bytes(ans)
|
||||||
|
|
||||||
|
def encint(value, forward=True):
|
||||||
|
'''
|
||||||
|
Some parts of the Mobipocket format encode data as variable-width integers.
|
||||||
|
These integers are represented big-endian with 7 bits per byte in bits 1-7.
|
||||||
|
They may be either forward-encoded, in which case only the first byte has bit 8 set,
|
||||||
|
or backward-encoded, in which case only the last byte has bit 8 set.
|
||||||
|
For example, the number 0x11111 would be represented forward-encoded as:
|
||||||
|
|
||||||
|
0x04 0x22 0x91
|
||||||
|
|
||||||
|
And backward-encoded as:
|
||||||
|
|
||||||
|
0x84 0x22 0x11
|
||||||
|
|
||||||
|
This function encodes the integer ``value`` as a variable width integer and
|
||||||
|
returns the bytestring corresponding to it.
|
||||||
|
'''
|
||||||
|
# Encode vwi
|
||||||
|
byts = bytearray()
|
||||||
|
while True:
|
||||||
|
b = value & 0b1111111
|
||||||
|
value >>= 7
|
||||||
|
byts.append(b)
|
||||||
|
if value == 0:
|
||||||
|
break
|
||||||
|
byts[0 if forward else -1] |= 0b10000000
|
||||||
|
return bytes(byts)
|
||||||
|
|
||||||
|
def rescale_image(data, maxsizeb=IMAGE_MAX_SIZE, dimen=None):
|
||||||
|
'''
|
||||||
|
Convert image setting all transparent pixels to white and changing format
|
||||||
|
to JPEG. Ensure the resultant image has a byte size less than
|
||||||
|
maxsizeb.
|
||||||
|
|
||||||
|
If dimen is not None, generate a thumbnail of width=dimen, height=dimen
|
||||||
|
|
||||||
|
Returns the image as a bytestring
|
||||||
|
'''
|
||||||
|
if dimen is not None:
|
||||||
|
data = thumbnail(data, width=dimen, height=dimen,
|
||||||
|
compression_quality=90)[-1]
|
||||||
|
else:
|
||||||
|
# Replace transparent pixels with white pixels and convert to JPEG
|
||||||
|
data = save_cover_data_to(data, 'img.jpg', return_data=True)
|
||||||
|
if len(data) <= maxsizeb:
|
||||||
|
return data
|
||||||
|
orig_data = data
|
||||||
|
img = Image()
|
||||||
|
quality = 95
|
||||||
|
|
||||||
|
img.load(data)
|
||||||
|
while len(data) >= maxsizeb and quality >= 10:
|
||||||
|
quality -= 5
|
||||||
|
img.set_compression_quality(quality)
|
||||||
|
data = img.export('jpg')
|
||||||
|
if len(data) <= maxsizeb:
|
||||||
|
return data
|
||||||
|
orig_data = data
|
||||||
|
|
||||||
|
scale = 0.9
|
||||||
|
while len(data) >= maxsizeb and scale >= 0.05:
|
||||||
|
img = Image()
|
||||||
|
img.load(orig_data)
|
||||||
|
w, h = img.size
|
||||||
|
img.size = (int(scale*w), int(scale*h))
|
||||||
|
img.set_compression_quality(quality)
|
||||||
|
data = img.export('jpg')
|
||||||
|
scale -= 0.05
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
@ -7,8 +7,8 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import re, os
|
import re, os
|
||||||
|
|
||||||
from PyQt4.QtCore import SIGNAL, Qt, pyqtSignal
|
from PyQt4.QtCore import SIGNAL, Qt, pyqtSignal
|
||||||
from PyQt4.QtGui import QDialog, QWidget, QDialogButtonBox, \
|
from PyQt4.QtGui import (QDialog, QWidget, QDialogButtonBox,
|
||||||
QBrush, QTextCursor, QTextEdit
|
QBrush, QTextCursor, QTextEdit)
|
||||||
|
|
||||||
from calibre.gui2.convert.regex_builder_ui import Ui_RegexBuilder
|
from calibre.gui2.convert.regex_builder_ui import Ui_RegexBuilder
|
||||||
from calibre.gui2.convert.xexp_edit_ui import Ui_Form as Ui_Edit
|
from calibre.gui2.convert.xexp_edit_ui import Ui_Form as Ui_Edit
|
||||||
@ -16,6 +16,7 @@ from calibre.gui2 import error_dialog, choose_files
|
|||||||
from calibre.ebooks.oeb.iterator import EbookIterator
|
from calibre.ebooks.oeb.iterator import EbookIterator
|
||||||
from calibre.ebooks.conversion.preprocess import HTMLPreProcessor
|
from calibre.ebooks.conversion.preprocess import HTMLPreProcessor
|
||||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||||
|
from calibre.constants import iswindows
|
||||||
|
|
||||||
class RegexBuilder(QDialog, Ui_RegexBuilder):
|
class RegexBuilder(QDialog, Ui_RegexBuilder):
|
||||||
|
|
||||||
@ -134,8 +135,16 @@ class RegexBuilder(QDialog, Ui_RegexBuilder):
|
|||||||
_('Cannot build regex using the GUI builder without a book.'),
|
_('Cannot build regex using the GUI builder without a book.'),
|
||||||
show=True)
|
show=True)
|
||||||
return False
|
return False
|
||||||
fpath = db.format(book_id, format, index_is_id=True,
|
try:
|
||||||
as_path=True)
|
fpath = db.format(book_id, format, index_is_id=True,
|
||||||
|
as_path=True)
|
||||||
|
except OSError:
|
||||||
|
if iswindows:
|
||||||
|
error_dialog(self, _('Could not open file'),
|
||||||
|
_('Could not open file, do you have it open in'
|
||||||
|
' another program?'), show=True)
|
||||||
|
return False
|
||||||
|
raise
|
||||||
try:
|
try:
|
||||||
self.open_book(fpath)
|
self.open_book(fpath)
|
||||||
finally:
|
finally:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user