%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/ebooks/mobi/writer2/ |
| Current File : //usr/lib/calibre/calibre/ebooks/mobi/writer2/resources.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
from PIL import Image, ImageOps
from io import BytesIO
from calibre.ebooks.mobi import MAX_THUMB_DIMEN, MAX_THUMB_SIZE
from calibre.ebooks.mobi.utils import (rescale_image, mobify_image,
write_font_record)
from calibre.ebooks import generate_masthead
from calibre.ebooks.oeb.base import OEB_RASTER_IMAGES
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.imghdr import what
from polyglot.builtins import iteritems
PLACEHOLDER_GIF = b'GIF89a\x01\x00\x01\x00\xf0\x00\x00\x00\x00\x00\xff\xff\xff!\xf9\x04\x01\x00\x00\x00\x00!\xfe calibre-placeholder-gif-for-azw3\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;' # noqa
def process_jpegs_for_amazon(data: bytes) -> bytes:
img = Image.open(BytesIO(data))
if img.format == 'JPEG':
# Amazon's MOBI renderer can't render JPEG images without JFIF metadata
# and images with EXIF data dont get displayed on the cover screen
changed = not img.info
if hasattr(img, '_getexif') and img._getexif():
changed = True
img = ImageOps.exif_transpose(img)
if changed:
out = BytesIO()
img.save(out, 'JPEG')
data = out.getvalue()
return data
class Resources:
def __init__(self, oeb, opts, is_periodical, add_fonts=False,
process_images=True):
self.oeb, self.log, self.opts = oeb, oeb.log, opts
self.is_periodical = is_periodical
self.process_images = process_images
self.item_map = {}
self.records = []
self.mime_map = {}
self.masthead_offset = 0
self.used_image_indices = set()
self.image_indices = set()
self.cover_offset = self.thumbnail_offset = None
self.has_fonts = False
self.add_resources(add_fonts)
def process_image(self, data):
if not self.process_images:
return process_jpegs_for_amazon(data)
func = mobify_image if self.opts.mobi_keep_original_images else rescale_image
try:
return process_jpegs_for_amazon(func(data))
except Exception:
if 'png' != what(None, data):
raise
with PersistentTemporaryFile(suffix='.png') as pt:
pt.write(data)
try:
from calibre.utils.img import optimize_png
optimize_png(pt.name)
data = lopen(pt.name, 'rb').read()
finally:
os.remove(pt.name)
return func(data)
def add_resources(self, add_fonts):
oeb = self.oeb
oeb.logger.info('Serializing resources...')
index = 1
mh_href = None
if 'masthead' in oeb.guide and oeb.guide['masthead'].href:
mh_href = oeb.guide['masthead'].href
self.records.append(None)
index += 1
self.used_image_indices.add(0)
self.image_indices.add(0)
elif self.is_periodical:
# Generate a default masthead
data = generate_masthead(str(self.oeb.metadata['title'][0]))
self.records.append(data)
self.used_image_indices.add(0)
self.image_indices.add(0)
index += 1
cover_href = self.cover_offset = self.thumbnail_offset = None
if (oeb.metadata.cover and
str(oeb.metadata.cover[0]) in oeb.manifest.ids):
cover_id = str(oeb.metadata.cover[0])
item = oeb.manifest.ids[cover_id]
cover_href = item.href
for item in self.oeb.manifest.values():
if item.media_type not in OEB_RASTER_IMAGES:
continue
if item.media_type.lower() == 'image/webp':
self.convert_webp(item)
try:
data = self.process_image(item.data)
except Exception:
self.log.warn('Bad image file %r' % item.href)
continue
else:
if mh_href and item.href == mh_href:
self.records[0] = data
continue
self.image_indices.add(len(self.records))
self.records.append(data)
self.item_map[item.href] = index
self.mime_map[item.href] = 'image/%s'%what(None, data)
index += 1
if cover_href and item.href == cover_href:
self.cover_offset = self.item_map[item.href] - 1
self.used_image_indices.add(self.cover_offset)
try:
tdata = rescale_image(data, dimen=MAX_THUMB_DIMEN, maxsizeb=MAX_THUMB_SIZE)
except:
self.log.warn('Failed to generate thumbnail')
else:
self.image_indices.add(len(self.records))
self.records.append(tdata)
self.thumbnail_offset = index - 1
self.used_image_indices.add(self.thumbnail_offset)
index += 1
finally:
item.unload_data_from_memory()
if add_fonts:
for item in self.oeb.manifest.values():
if item.href and item.href.rpartition('.')[-1].lower() in {
'ttf', 'otf'} and isinstance(item.data, bytes):
self.records.append(write_font_record(item.data))
self.item_map[item.href] = len(self.records)
self.has_fonts = True
def convert_webp(self, item):
from calibre.utils.img import image_and_format_from_data, image_to_data
img, fmt = image_and_format_from_data(item.data)
if fmt == 'webp' and not img.isNull():
self.log.info(f'Converting WebP image {item.href} to PNG')
item.data = image_to_data(img, fmt='PNG')
item.media_type = 'image/png'
def add_extra_images(self):
'''
Add any images that were created after the call to add_resources()
'''
for item in self.oeb.manifest.values():
if (item.media_type not in OEB_RASTER_IMAGES or item.href in self.item_map):
continue
try:
data = self.process_image(item.data)
except:
self.log.warn('Bad image file %r' % item.href)
else:
self.records.append(data)
self.item_map[item.href] = len(self.records)
finally:
item.unload_data_from_memory()
def serialize(self, records, used_images):
used_image_indices = self.used_image_indices | {
v-1 for k, v in iteritems(self.item_map) if k in used_images}
for i in self.image_indices-used_image_indices:
self.records[i] = PLACEHOLDER_GIF
records.extend(self.records)
def __bool__(self):
return bool(self.records)
__nonzero__ = __bool__