Coverage for src/epublib/ncx/reset.py: 89%

46 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-18 16:07 -0300

1from pathlib import Path 

2 

3from bs4.element import NamespacedAttribute 

4 

5from epublib.exceptions import EPUBError 

6from epublib.ncx.resource import NCXFile 

7from epublib.package.metadata import ValuedMetadataItem 

8from epublib.types import BookProtocol 

9 

10ncx_template = """<?xml version="1.0" encoding="UTF-8"?> 

11<ncx version="2005-1" {lang_attr} xmlns="http://www.daisy.org/z3986/2005/ncx/"> 

12<head></head> 

13<docTitle><text>{title}</text></docTitle> 

14<navMap></navMap> 

15</ncx 

16""" 

17 

18 

19def get_minimal_ncx_content(title: str, lang: str | None) -> bytes: 

20 """ 

21 Get a minimal NCX file content with the given title and language. 

22 Caution: the minimality of this template is in regard to the parsing 

23 available in this library. To get a minimal valid NCX file, consider 

24 using `EPUB.generate_ncx` instead. 

25 """ 

26 if lang: 

27 lang_attr = f'xml:lang="{lang}"' 

28 else: 

29 lang_attr = "" 

30 return ncx_template.format(title=title, lang_attr=lang_attr).encode() 

31 

32 

33def generate_ncx(book: BookProtocol, filename: str | Path | None = None) -> NCXFile: 

34 if filename is None: 

35 filename = book.base_dir / "toc.ncx" 

36 

37 if not book.metadata.title: 

38 raise EPUBError("Can't generate NCX without book title in metadata") 

39 

40 if not (book.nav and book.nav.toc): 

41 raise EPUBError("Can't generate NCX without Navigation Document with TOC") 

42 

43 if book.ncx is not None: 

44 raise EPUBError( 

45 "Can't generate NCX as it already exists. Try " 

46 f"{book.__class__.__name__}.reset_ncx() instead" 

47 ) 

48 

49 ncx = NCXFile( 

50 get_minimal_ncx_content( 

51 book.metadata.title, 

52 book.metadata.language, 

53 ), 

54 filename, 

55 ) 

56 

57 ncx = reset_ncx(book, ncx) 

58 book.resources.add(ncx) 

59 book.spine.tag["toc"] = book.manifest[ncx.filename].id 

60 return ncx 

61 

62 

63def reset_ncx(book: BookProtocol, ncx: NCXFile | None = None) -> NCXFile: 

64 if not book.metadata.title: 

65 raise EPUBError("Can't reset NCX without book title in metadata") 

66 

67 if not (book.nav and book.nav.toc): 

68 raise EPUBError("Can't reset NCX without Navigation Document with TOC") 

69 

70 if ncx is None: 

71 ncx = book.ncx 

72 

73 if ncx is None: 

74 return generate_ncx(book) 

75 

76 ncx.title.text = book.metadata.title 

77 creator = book.metadata.get("creator") 

78 if isinstance(creator, ValuedMetadataItem): 

79 __ = ncx.add_author(creator.value) 

80 

81 if book.metadata.language: 

82 ncx.soup.ncx[NamespacedAttribute("xml", "lang")] = book.metadata.language 

83 

84 __ = ncx.sync_toc(book.nav) 

85 if book.nav.page_list: 

86 __ = ncx.sync_page_list(book.nav) 

87 __ = ncx.sync_head(book.metadata) 

88 

89 return ncx