Fix toc from climbing too high when going from h2 to h4 to h2 etc.

Can you believe there was a bug amidst all these wonderful comments?
The code previously assumed that climbing .parent corresponded to
climbing from hX to hX-1, but this wasn't true when the headers
themselves skipped a level. After creating some toc for h2 then h4,
then encountering a new h2, it would try to climb back two levels to
go from 4 to 2, but actually there was only ever one level between them.
This commit is contained in:
Ethan Dalool 2020-02-04 16:40:14 -08:00
parent 92d595c00d
commit c56e0ec13e

View file

@ -1029,8 +1029,9 @@ class Epub:
# have different structural requirements. The attributes that I'm using # have different structural requirements. The attributes that I'm using
# in this initial toc object DO NOT represent any part of the epub format. # in this initial toc object DO NOT represent any part of the epub format.
toc = new_list(root=True) toc = new_list(root=True)
current_level = None
current_list = toc.ol current_list = toc.ol
current_list['level'] = None
spine = self.get_spine_order(linear_only=linear_only) spine = self.get_spine_order(linear_only=linear_only)
spine = [s for s in spine if s != nav_id] spine = [s for s in spine if s != nav_id]
@ -1055,11 +1056,10 @@ class Epub:
relative = file_path.relative_to(ncx_filepath.parent, simple=True) relative = file_path.relative_to(ncx_filepath.parent, simple=True)
toc_line['ncx_anchor'] = f'{relative}#{header["id"]}' toc_line['ncx_anchor'] = f'{relative}#{header["id"]}'
if current_level is None: if current_list['level'] is None:
current_level = level current_list['level'] = level
while level < current_level: while level < current_list['level']:
current_level -= 1
# Because the sub-<ol> are actually a child of the last # Because the sub-<ol> are actually a child of the last
# <li> of the previous <ol>, we must .parent twice. # <li> of the previous <ol>, we must .parent twice.
# The second .parent is conditional because if the current # The second .parent is conditional because if the current
@ -1079,11 +1079,10 @@ class Epub:
# In the resulting toc, that initial h4 would have the same # In the resulting toc, that initial h4 would have the same
# toc depth as the later h1 since it never had parents. # toc depth as the later h1 since it never had parents.
if current_list == toc: if current_list == toc:
current_level = level current_list['level'] = level
current_list = toc.ol current_list = toc.ol
if level > current_level: if level > current_list['level']:
current_level = level
# In order to properly render nested <ol>, you're supposed # In order to properly render nested <ol>, you're supposed
# to make the new <ol> a child of the last <li> of the # to make the new <ol> a child of the last <li> of the
# previous <ol>. NOT a child of the prev <ol> directly. # previous <ol>. NOT a child of the prev <ol> directly.
@ -1091,6 +1090,7 @@ class Epub:
# first <li> this condition can never occur, and new <ol>s # first <li> this condition can never occur, and new <ol>s
# always receive a child right after being created. # always receive a child right after being created.
_l = new_list() _l = new_list()
_l['level'] = level
final_li = list(current_list.children)[-1] final_li = list(current_list.children)[-1]
final_li.append(_l) final_li.append(_l)
current_list = _l current_list = _l
@ -1100,6 +1100,9 @@ class Epub:
# We have to save the id="toc_X" that we gave to all the headers. # We have to save the id="toc_X" that we gave to all the headers.
self.write_file(file_id, soup) self.write_file(file_id, soup)
for ol in toc.find_all('ol'):
del ol['level']
if nav_id: if nav_id:
self._set_nav_toc(nav_id, copy.copy(toc)) self._set_nav_toc(nav_id, copy.copy(toc))