#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
from __future__ import unicode_literals, division, absolute_import, print_function
from .compatibility_utils import unicode_str
from .unipath import pathof
import os
import imghdr
import struct
# note: struct pack, unpack, unpack_from all require bytestring format
# data all the way up to at least python 2.7.5, python 3 okay with bytestring
USE_SVG_WRAPPER = True
""" Set to True to use svg wrapper for default. """
FORCE_DEFAULT_TITLE = False
""" Set to True to force to use the default title. """
COVER_PAGE_FINENAME = 'cover_page.xhtml'
""" The name for the cover page. """
DEFAULT_TITLE = 'Cover'
""" The default title for the cover page. """
MAX_WIDTH = 4096
""" The max width for the svg cover page. """
MAX_HEIGHT = 4096
""" The max height for the svg cover page. """
def get_image_type(imgname, imgdata=None):
imgtype = unicode_str(imghdr.what(pathof(imgname), imgdata))
# imghdr only checks for JFIF or Exif JPEG files. Apparently, there are some
# with only the magic JPEG bytes out there...
# ImageMagick handles those, so, do it too.
if imgtype is None:
if imgdata is None:
with open(pathof(imgname), 'rb') as f:
imgdata = f.read()
if imgdata[0:2] == b'\xFF\xD8':
# Get last non-null bytes
last = len(imgdata)
while (imgdata[last-1:last] == b'\x00'):
last-=1
# Be extra safe, check the trailing bytes, too.
if imgdata[last-2:last] == b'\xFF\xD9':
imgtype = "jpeg"
return imgtype
def get_image_size(imgname, imgdata=None):
'''Determine the image type of imgname (or imgdata) and return its size.
Originally,
Determine the image type of fhandle and return its size.
from draco'''
if imgdata is None:
fhandle = open(pathof(imgname), 'rb')
head = fhandle.read(24)
else:
head = imgdata[0:24]
if len(head) != 24:
return
imgtype = get_image_type(imgname, imgdata)
if imgtype == 'png':
check = struct.unpack(b'>i', head[4:8])[0]
if check != 0x0d0a1a0a:
return
width, height = struct.unpack(b'>ii', head[16:24])
elif imgtype == 'gif':
width, height = struct.unpack(b'<HH', head[6:10])
elif imgtype == 'jpeg' and imgdata is None:
try:
fhandle.seek(0) # Read 0xff next
size = 2
ftype = 0
while not 0xc0 <= ftype <= 0xcf:
fhandle.seek(size, 1)
byte = fhandle.read(1)
while ord(byte) == 0xff:
byte = fhandle.read(1)
ftype = ord(byte)
size = struct.unpack(b'>H', fhandle.read(2))[0] - 2
# We are at a SOFn block
fhandle.seek(1, 1) # Skip `precision' byte.
height, width = struct.unpack(b'>HH', fhandle.read(4))
except Exception: # IGNORE:W0703
return
elif imgtype == 'jpeg' and imgdata is not None:
try:
pos = 0
size = 2
ftype = 0
while not 0xc0 <= ftype <= 0xcf:
pos += size
byte = imgdata[pos:pos+1]
pos += 1
while ord(byte) == 0xff:
byte = imgdata[pos:pos+1]
pos += 1
ftype = ord(byte)
size = struct.unpack(b'>H', imgdata[pos:pos+2])[0] - 2
pos += 2
# We are at a SOFn block
pos += 1 # Skip `precision' byte.
height, width = struct.unpack(b'>HH', imgdata[pos:pos+4])
pos += 4
except Exception: # IGNORE:W0703
return
else:
return
return width, height
# XXX experimental
class CoverProcessor(object):
"""Create a cover page.
"""
def __init__(self, files, metadata, rscnames, imgname=None, imgdata=None):
self.files = files
self.metadata = metadata
self.rscnames = rscnames
self.cover_page = COVER_PAGE_FINENAME
self.use_svg = USE_SVG_WRAPPER # Use svg wrapper.
self.lang = metadata.get('Language', ['en'])[0]
# This should ensure that if the methods to find the cover image's
# dimensions should fail for any reason, the SVG routine will not be used.
[self.width, self.height] = (-1,-1)
if FORCE_DEFAULT_TITLE:
self.title = DEFAULT_TITLE
else:
self.title = metadata.get('Title', [DEFAULT_TITLE])[0]
self.cover_image = None
if imgname is not None:
self.cover_image = imgname
elif 'CoverOffset' in metadata:
imageNumber = int(metadata['CoverOffset'][0])
cover_image = self.rscnames[imageNumber]
if cover_image is not None:
self.cover_image = cover_image
else:
print('Warning: Cannot identify the cover image.')
if self.use_svg:
try:
if imgdata is None:
fname = os.path.join(files.imgdir, self.cover_image)
[self.width, self.height] = get_image_size(fname)
else:
[self.width, self.height] = get_image_size(None, imgdata)
except:
self.use_svg = False
width = self.width
height = self.height
if width < 0 or height < 0 or width > MAX_WIDTH or height > MAX_HEIGHT:
self.use_svg = False
return
def getImageName(self):
return self.cover_image
def getXHTMLName(self):
return self.cover_page
def buildXHTML(self):
print('Building a cover page.')
files = self.files
cover_image = self.cover_image
title = self.title
lang = self.lang
image_dir = os.path.normpath(os.path.relpath(files.k8images, files.k8text))
image_path = os.path.join(image_dir, cover_image).replace('\\', '/')
if not self.use_svg:
data = ''
data += '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html>'
data += '<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"'
data += ' xml:lang="{:s}">\n'.format(lang)
data += '<head>\n<title>{:s}</title>\n'.format(title)
data += '<style type="text/css">\n'
data += 'body {\n margin: 0;\n padding: 0;\n text-align: center;\n}\n'
data += 'div {\n height: 100%;\n width: 100%;\n text-align: center;\n page-break-inside: avoid;\n}\n'
data += 'img {\n display: inline-block;\n height: 100%;\n margin: 0 auto;\n}\n'
data += '</style>\n</head>\n'
data += '<body><div>\n'
data += ' <img src="{:s}" alt=""/>\n'.format(image_path)
data += '</div></body>\n</html>'
else:
width = self.width
height = self.height
viewBox = "0 0 {0:d} {1:d}".format(width, height)
data = ''
data += '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html>'
data += '<html xmlns="http://www.w3.org/1999/xhtml"'
data += ' xml:lang="{:s}">\n'.format(lang)
data += '<head>\n <title>{:s}</title>\n'.format(title)
data += '<style type="text/css">\n'
data += 'svg {padding: 0pt; margin:0pt}\n'
data += 'body { text-align: center; padding:0pt; margin: 0pt; }\n'
data += '</style>\n</head>\n'
data += '<body>\n <div>\n'
data += ' <svg xmlns="http://www.w3.org/2000/svg" height="100%" preserveAspectRatio="xMidYMid meet"'
data += ' version="1.1" viewBox="{0:s}" width="100%" xmlns:xlink="http://www.w3.org/1999/xlink">\n'.format(viewBox)
data += ' <image height="{0}" width="{1}" xlink:href="{2}"/>\n'.format(height, width, image_path)
data += ' </svg>\n'
data += ' </div>\n</body>\n</html>'
return data
def writeXHTML(self):
files = self.files
cover_page = self.cover_page
data = self.buildXHTML()
outfile = os.path.join(files.k8text, cover_page)
if os.path.exists(pathof(outfile)):
print('Warning: {:s} already exists.'.format(cover_page))
os.remove(pathof(outfile))
with open(pathof(outfile), 'wb') as f:
f.write(data.encode('utf-8'))
return
def guide_toxml(self):
files = self.files
text_dir = os.path.relpath(files.k8text, files.k8oebps)
data = '<reference type="cover" title="Cover" href="{:s}/{:s}" />\n'.format(
text_dir, self.cover_page)
return data