diff --git a/src/calibre/ebooks/oeb/polish/kepubify.py b/src/calibre/ebooks/oeb/polish/kepubify.py index c97eec01a1..c56c4fbf75 100644 --- a/src/calibre/ebooks/oeb/polish/kepubify.py +++ b/src/calibre/ebooks/oeb/polish/kepubify.py @@ -315,7 +315,7 @@ def add_dummy_title_page(container: Container, cover_image_name: str) -> None: __CONTENT__ ''' - titlepage_name = container.add_file(f'{DUMMY_TITLE_PAGE_NAME}.html', modify_name_if_needed=True) + titlepage_name = container.add_file(f'{DUMMY_TITLE_PAGE_NAME}.xhtml', modify_name_if_needed=True, spine_index=0) if cover_image_name: cover_href = container.name_to_href(cover_image_name, titlepage_name) html = html.replace('__CONTENT__', f'cover') @@ -329,10 +329,11 @@ def add_dummy_title_page(container: Container, cover_image_name: str) -> None: with container.open(titlepage_name, 'w') as f: f.write(html) container.apply_unique_properties(titlepage_name, 'calibre:title-page') + return titlepage_name def remove_dummy_title_page(container: Container) -> None: - for name, is_linear in container.spine_names(): + for name, is_linear in container.spine_names: if is_linear: if DUMMY_TITLE_PAGE_NAME in name: container.remove_item(name) @@ -400,7 +401,7 @@ def kepubify_path(path, outpath='', max_workers=0, allow_overwrite=False, opts: while not allow_overwrite and outpath == path: c += 1 outpath = f'{base} - {c}.kepub' - container.commit(output=outpath) + container.commit(outpath=outpath) return outpath diff --git a/src/calibre/ebooks/oeb/polish/tests/base.py b/src/calibre/ebooks/oeb/polish/tests/base.py index f741142b7a..4cec50a4a3 100644 --- a/src/calibre/ebooks/oeb/polish/tests/base.py +++ b/src/calibre/ebooks/oeb/polish/tests/base.py @@ -58,24 +58,29 @@ def add_resources(raw, rmap): return raw +def setup_simple_book(src): + with open(src, 'rb') as sf: + raw = sf.read().decode('utf-8') + raw = add_resources(raw, { + 'LMONOI': P('fonts/liberation/LiberationMono-Italic.ttf'), + 'LMONOR': P('fonts/liberation/LiberationMono-Regular.ttf'), + 'IMAGE1': I('marked.png'), + 'IMAGE2': I('textures/light_wood.png'), + }) + shutil.copy2(I('lt.png'), '.') + x = 'index.html' + with open(x, 'wb') as f: + f.write(raw.encode('utf-8')) + return x + + def get_simple_book(fmt='epub'): cache = get_cache() ans = os.path.join(cache, 'simple.'+fmt) src = os.path.join(os.path.dirname(__file__), 'simple.html') if needs_recompile(ans, src): with TemporaryDirectory('bpt') as tdir, CurrentDir(tdir): - with open(src, 'rb') as sf: - raw = sf.read().decode('utf-8') - raw = add_resources(raw, { - 'LMONOI': P('fonts/liberation/LiberationMono-Italic.ttf'), - 'LMONOR': P('fonts/liberation/LiberationMono-Regular.ttf'), - 'IMAGE1': I('marked.png'), - 'IMAGE2': I('textures/light_wood.png'), - }) - shutil.copy2(I('lt.png'), '.') - x = 'index.html' - with open(x, 'wb') as f: - f.write(raw.encode('utf-8')) + x = setup_simple_book(src) build_book(x, ans, args=[ '--level1-toc=//h:h2', '--language=en', '--authors=Kovid Goyal', '--cover=lt.png']) return ans @@ -99,6 +104,29 @@ def get_split_book(fmt='epub'): return ans +def get_book_for_kepubify(has_cover=True, epub_version='3'): + cache = get_cache() + ans = os.path.join(cache, f'kepubify-{has_cover}-{epub_version}.epub') + src = os.path.join(os.path.dirname(__file__), 'simple.html') + if needs_recompile(ans, src): + with TemporaryDirectory('bpt') as tdir, CurrentDir(tdir): + index_html = setup_simple_book(src) + args = ['--level1-toc=//h:h2', '--language=en', '--authors=Kovid Goyal', f'--epub-version={epub_version}'] + if has_cover: + args.append('--cover=lt.png') + else: + args.append('--no-default-epub-cover') + build_book(index_html, ans, args=args) + c = pc.get_container(ans) + with c.open('page_styles.css', 'r+') as f: + css = f.read() + css += '\n\ndiv { widows: 13; orphans: 12; color: red; }' + f.seek(0), f.truncate(), f.write(css) + c.commit() + + return ans + + devnull = DevNull() diff --git a/src/calibre/ebooks/oeb/polish/tests/kepubify.py b/src/calibre/ebooks/oeb/polish/tests/kepubify.py index e70dff9a02..89dfbe4fe6 100644 --- a/src/calibre/ebooks/oeb/polish/tests/kepubify.py +++ b/src/calibre/ebooks/oeb/polish/tests/kepubify.py @@ -2,13 +2,33 @@ # License: GPLv3 Copyright: 2025, Kovid Goyal -from calibre.ebooks.oeb.polish.kepubify import Options, kepubify_html_data, remove_kobo_markup_from_html, serialize_html +from calibre.ebooks.oeb.polish.container import get_container +from calibre.ebooks.oeb.polish.kepubify import DUMMY_TITLE_PAGE_NAME, Options, kepubify_html_data, kepubify_path, remove_kobo_markup_from_html, serialize_html from calibre.ebooks.oeb.polish.parsing import parse -from calibre.ebooks.oeb.polish.tests.base import BaseTest +from calibre.ebooks.oeb.polish.tests.base import BaseTest, get_book_for_kepubify class KepubifyTests(BaseTest): + def test_kepubify_container(self): + def b(has_cover=True, epub_version='3'): + path = get_book_for_kepubify(has_cover=has_cover, epub_version=epub_version) + opts = Options() + opts = opts._replace(remove_widows_and_orphans=True) + opts = opts._replace(remove_at_page_rules=True) + outpath = kepubify_path(path, opts=opts, allow_overwrite=True) + c = get_container(outpath, tweak_mode=True) + spine_names = tuple(n for n, is_linear in c.spine_names) + cname = 'titlepage.xhtml' if has_cover else f'{DUMMY_TITLE_PAGE_NAME}.xhtml' + self.assertEqual(spine_names, (cname, 'index_split_000.html', 'index_split_001.html')) + ps = c.open('page_styles.css', 'r').read() + for q in ('@page', 'widows', 'orphans'): + self.assertNotIn(q, ps) + cimage = ('cover.png',) if has_cover else () + self.assertEqual(cimage, tuple(c.manifest_items_with_property('cover-image'))) + b() + b(has_cover=False) + def test_kepubify_html(self): prefix = '''