aerkalov/ebooklib

No styles for cover?

dajames-mk1 opened this issue · 5 comments

I'm trying to create ePub versions of a periodical using ebooklib. I want to set a style for the cover page, and to do that I've set a cover template like this (it's based on the default cover template):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="en" xml:lang="en">
<head>
    <link href="../styles/mystylesheet.css" rel="stylesheet" type="text/css"/>
</head>
 <body>
   <img class="cover" src="" alt="" />
 </body>
</html>

However, when I examine the created ePub file I find this:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" epub:prefix="z3998: http://www.daisy.org/z3998/2012/vocab/structure/#" lang="en-GB" xml:lang="en-GB">
  <head>
    <title>Cover</title>
  </head>
  <body><img class="cover" src="Cover169.png" alt="Cover"/>
 </body>
</html>

That is, the <link> tag I put into the <head> tag is missing.

Looking into this a bit more deeply, I find that the <head> section of the cover page is always reduced to just the title, even when using the default template (which contains some <style> tags that are removed).

Is it possible to actually set a style for the cover image?

I worked around this issue by creating my own cover page, using the following code (based on the code in the library)

def add_journal_cover( book, cover_name ):
    '''
    Add the cover to the journal

    book is the ebook
    cover_name is the name of the image file on disk for the cover.

    We do NOT simply use ebooklib's function for this, as it loses the syle-sheet
    reference from the <head> of the page, and sets the cover to be non-linear
    in the spine listing (so Calibre shows it at the end!)
    '''
    # Set the book cover template
    cover_template = six.b('''<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="en" xml:lang="en">
     <body>
       <div id="cover-image">
       <p><img class="cover" src="" alt="" /></p>
       </div>
     </body>
    </html>''')

    book.set_template( name='cover', value=cover_template )

    # Add the cover itself
    # First check that the specified image exists
    cover_image_path = Path( cover_name )
    if not cover_image_path.exists():
        print( '*** Cover image', cover_name, 'does not exist ***')
        exit(1)

    # internal filename for image - e.g. media/Cover01-2.png
    internal_image_name = 'media/'+cover_name

    # internal filename for html - e.g. text/Cover01-2.xhtml
    internal_html_name = 'text/'+cover_image_path.stem+'.xhtml'

    # Add the cover image to the book
    cover = epub.EpubCover( uid='cover-img', file_name=internal_image_name )
    cover.content = cover_image_path.read_bytes()
    book.add_item( cover )

    # Add the cover HTML - explicitly but using the template we gave ebooklib
    cover_page = epub.EpubCoverHtml( 
        uid='cover', 
        file_name=internal_html_name, 
        image_name='../'+internal_image_name )
    cover_page.add_item( doc_style_ref )
    cover_page.is_linear = True     # default is False
    book.add_item( cover_page )
    book.add_metadata( None, 'meta', '', epub.OrderedDict([('name', 'cover'), ('content', 'cover-img')]) )

(doc_style_ref is a global EpubItem referencing the CSS at ../style/mystyle.css, used by all the chapters in the journal)

This seems to work, but it feels ... fragile ... in the face of possible future changes to the library.

wdpm commented

Hey, boy. I met the same problem before. This problem can be categorized as:

How to declare external css file to link to a certain EpubHtml instance?

The EpubHtml instance can be nav.xhtml or cover.xhtml or chapter-xxxx.xhtml.
Finally I found a workaround. I post it to here, hope thas helps.

  • ebooklib 0.17.1
# add navigation files
book.add_item(epub.EpubNcx())
book.add_item(epub.EpubNav())

# add cover css
cover_style = 'body { background-color: #e1e1e1;}'
cover_css = epub.EpubItem(uid="style_cover", file_name="style/cover.css", media_type="text/css", content=cover_style)
cover_html = book.get_item_with_id('cover')
cover_html.add_item(cover_css)
book.add_item(cover_css)

# write book

I think the key step is this line:

cover_html.add_item(cover_css)

It let cover_html instance to add a css item, because cover_html is an EpubHtml instance and each EpubHtml instance has add_item() function.

references

class EpubHtml(EpubItem):

    def add_item(self, item):
        """
        Add other item to this document. It will create additional links according to the item type.

        :Args:
          - item: item we want to add defined as instance of EpubItem
        """
        if item.get_type() == ebooklib.ITEM_STYLE:
            self.add_link(href=item.get_name(), rel='stylesheet', type='text/css')

        if item.get_type() == ebooklib.ITEM_SCRIPT:
            self.add_link(src=item.get_name(), type='text/javascript')

If you want full control over your cover page then you should just recreate what method set_cover is doing but if you just want to add CSS style then you can do it this way.

EpubCoverHtml behaves exactly like EpubHtml and will ignore whatever you have defined in header tag inside your template. To add something to it just use EpubHtml.add_item() method. So @wdpm 's workaround is the best solution for it.

For example you could also combine .set_cover() and add_item():

    # add cover image
    book.set_cover("image.jpg", open('cover.jpg', 'rb').read())

    # html page with uid=cover is created and added to the book
    # just fetch it now
    cover = book.get_item_with_id('cover')

    # create new CSS file
    st = 'BODY { background-color: #ff00ff; }'
    cover_css = epub.EpubItem(uid='cover_style', file_name="style/cover.css", media_type="text/css", content=st)

    # add that file to the book
    book.add_item(cover_css)

    # add that file to the Cover page so we can add it as a <link> when generating final html for the cover page
    cover.add_item(cover_css)
wdpm commented

@aerkalov Thank for you reply.
Although the solution above is a feasible workaround, but at the very start I had a lot of difficulties and tried a lot.
Because I can't find any document about setting cover/nav styles. Wouldn't it be better to add this in the documentation tutorial?
I think that setting styles is a very common functional requirement for using ebooklib.