import cobble from ..documents import numbering_level from .styles_xml import Styles def read_numbering_xml_element(element, styles): abstract_nums = _read_abstract_nums(element) nums = _read_nums(element) return Numbering(abstract_nums=abstract_nums, nums=nums, styles=styles) def _read_abstract_nums(element): abstract_num_elements = element.find_children("w:abstractNum") return dict(map(_read_abstract_num, abstract_num_elements)) def _read_abstract_num(element): abstract_num_id = element.attributes.get("w:abstractNumId") levels = _read_abstract_num_levels(element) num_style_link = element.find_child_or_null("w:numStyleLink").attributes.get("w:val") return abstract_num_id, _AbstractNum(levels=levels, num_style_link=num_style_link) @cobble.data class _AbstractNum(object): levels = cobble.field() num_style_link = cobble.field() @cobble.data class _AbstractNumLevel(object): level_index = cobble.field() is_ordered = cobble.field() paragraph_style_id = cobble.field() def _read_abstract_num_levels(element): levels = map(_read_abstract_num_level, element.find_children("w:lvl")) return dict( (level.level_index, level) for level in levels ) def _read_abstract_num_level(element): level_index = element.attributes["w:ilvl"] num_fmt = element.find_child_or_null("w:numFmt").attributes.get("w:val") is_ordered = num_fmt != "bullet" paragraph_style_id = element.find_child_or_null("w:pStyle").attributes.get("w:val") return _AbstractNumLevel( level_index=level_index, is_ordered=is_ordered, paragraph_style_id=paragraph_style_id, ) def _read_nums(element): num_elements = element.find_children("w:num") return dict( _read_num(num_element) for num_element in num_elements ) def _read_num(element): num_id = element.attributes.get("w:numId") abstract_num_id = element.find_child_or_null("w:abstractNumId").attributes["w:val"] return num_id, _Num(abstract_num_id=abstract_num_id) @cobble.data class _Num(object): abstract_num_id = cobble.field() class Numbering(object): def __init__(self, abstract_nums, nums, styles): self._abstract_nums = abstract_nums self._levels_by_paragraph_style_id = dict( (level.paragraph_style_id, self._to_numbering_level(level)) for abstract_num in abstract_nums.values() for level in abstract_num.levels.values() if level.paragraph_style_id is not None ) self._nums = nums self._styles = styles def find_level(self, num_id, level): num = self._nums.get(num_id) if num is None: return None else: abstract_num = self._abstract_nums.get(num.abstract_num_id) if abstract_num is None: return None elif abstract_num.num_style_link is None: return self._to_numbering_level(abstract_num.levels.get(level)) else: style = self._styles.find_numbering_style_by_id(abstract_num.num_style_link) return self.find_level(style.num_id, level) def find_level_by_paragraph_style_id(self, style_id): return self._levels_by_paragraph_style_id.get(style_id) def _to_numbering_level(self, abstract_num_level): if abstract_num_level is None: return None else: return numbering_level( level_index=abstract_num_level.level_index, is_ordered=abstract_num_level.is_ordered, ) Numbering.EMPTY = Numbering(abstract_nums={}, nums={}, styles=Styles.EMPTY)