mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
DOCX Input: Fix incorrect numbering being generated for numbered lists in some circumstances. Fixes #1301044 [DOCX conversion of numbered list](https://bugs.launchpad.net/calibre/+bug/1301044)
Apparently, list counters are associated with abstract numbering definitions and not numbering instances. Also add support for startOverride.
This commit is contained in:
parent
05cac89d8d
commit
664fe0aba7
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from collections import Counter
|
from collections import Counter, defaultdict
|
||||||
|
|
||||||
from lxml.html.builder import OL, UL, SPAN
|
from lxml.html.builder import OL, UL, SPAN
|
||||||
|
|
||||||
@ -135,8 +135,9 @@ class Level(object):
|
|||||||
|
|
||||||
class NumberingDefinition(object):
|
class NumberingDefinition(object):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None, an_id=None):
|
||||||
self.levels = {}
|
self.levels = {}
|
||||||
|
self.abstract_numbering_definition_id = an_id
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
for lvl in XPath('./w:lvl')(parent):
|
for lvl in XPath('./w:lvl')(parent):
|
||||||
try:
|
try:
|
||||||
@ -146,7 +147,7 @@ class NumberingDefinition(object):
|
|||||||
self.levels[ilvl] = Level(lvl)
|
self.levels[ilvl] = Level(lvl)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
ans = NumberingDefinition()
|
ans = NumberingDefinition(an_id=self.abstract_numbering_definition_id)
|
||||||
for l, lvl in self.levels.iteritems():
|
for l, lvl in self.levels.iteritems():
|
||||||
ans.levels[l] = lvl.copy()
|
ans.levels[l] = lvl.copy()
|
||||||
return ans
|
return ans
|
||||||
@ -156,7 +157,8 @@ class Numbering(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.definitions = {}
|
self.definitions = {}
|
||||||
self.instances = {}
|
self.instances = {}
|
||||||
self.counters = {}
|
self.counters = defaultdict(Counter)
|
||||||
|
self.starts = {}
|
||||||
self.pic_map = {}
|
self.pic_map = {}
|
||||||
|
|
||||||
def __call__(self, root, styles, rid_map):
|
def __call__(self, root, styles, rid_map):
|
||||||
@ -174,13 +176,24 @@ class Numbering(object):
|
|||||||
if nsl:
|
if nsl:
|
||||||
lazy_load[an_id] = get(nsl[0], 'w:val')
|
lazy_load[an_id] = get(nsl[0], 'w:val')
|
||||||
else:
|
else:
|
||||||
nd = NumberingDefinition(an)
|
nd = NumberingDefinition(an, an_id=an_id)
|
||||||
self.definitions[an_id] = nd
|
self.definitions[an_id] = nd
|
||||||
|
|
||||||
def create_instance(n, definition):
|
def create_instance(n, definition):
|
||||||
nd = definition.copy()
|
nd = definition.copy()
|
||||||
|
start_overrides = {}
|
||||||
for lo in XPath('./w:lvlOverride')(n):
|
for lo in XPath('./w:lvlOverride')(n):
|
||||||
ilvl = get(lo, 'w:ilvl')
|
try:
|
||||||
|
ilvl = int(get(lo, 'w:ilvl'))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
ilvl = None
|
||||||
|
for so in XPath('./w:startOverride[@w:val]')(lo):
|
||||||
|
try:
|
||||||
|
start_override = int(get(so, 'w:val'))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
start_overrides[ilvl] = start_override
|
||||||
for lvl in XPath('./w:lvl')(lo)[:1]:
|
for lvl in XPath('./w:lvl')(lo)[:1]:
|
||||||
nilvl = get(lvl, 'w:ilvl')
|
nilvl = get(lvl, 'w:ilvl')
|
||||||
ilvl = nilvl if ilvl is None else ilvl
|
ilvl = nilvl if ilvl is None else ilvl
|
||||||
@ -188,6 +201,11 @@ class Numbering(object):
|
|||||||
if alvl is None:
|
if alvl is None:
|
||||||
alvl = Level()
|
alvl = Level()
|
||||||
alvl.read_from_xml(lvl, override=True)
|
alvl.read_from_xml(lvl, override=True)
|
||||||
|
for ilvl, so in start_overrides.iteritems():
|
||||||
|
try:
|
||||||
|
nd.levels[ilvl].start = start_override
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
return nd
|
return nd
|
||||||
|
|
||||||
next_pass = {}
|
next_pass = {}
|
||||||
@ -213,7 +231,7 @@ class Numbering(object):
|
|||||||
self.instances[num_id] = create_instance(n, d)
|
self.instances[num_id] = create_instance(n, d)
|
||||||
|
|
||||||
for num_id, d in self.instances.iteritems():
|
for num_id, d in self.instances.iteritems():
|
||||||
self.counters[num_id] = Counter({lvl:d.levels[lvl].start for lvl in d.levels})
|
self.starts[num_id] = {lvl:d.levels[lvl].start for lvl in d.levels}
|
||||||
|
|
||||||
def get_pstyle(self, num_id, style_id):
|
def get_pstyle(self, num_id, style_id):
|
||||||
d = self.instances.get(num_id, None)
|
d = self.instances.get(num_id, None)
|
||||||
@ -236,12 +254,17 @@ class Numbering(object):
|
|||||||
counter[ilvl] = lvl.start
|
counter[ilvl] = lvl.start
|
||||||
|
|
||||||
def apply_markup(self, items, body, styles, object_map, images):
|
def apply_markup(self, items, body, styles, object_map, images):
|
||||||
|
seen_instances = set()
|
||||||
for p, num_id, ilvl in items:
|
for p, num_id, ilvl in items:
|
||||||
d = self.instances.get(num_id, None)
|
d = self.instances.get(num_id, None)
|
||||||
if d is not None:
|
if d is not None:
|
||||||
lvl = d.levels.get(ilvl, None)
|
lvl = d.levels.get(ilvl, None)
|
||||||
if lvl is not None:
|
if lvl is not None:
|
||||||
counter = self.counters[num_id]
|
an_id = d.abstract_numbering_definition_id
|
||||||
|
counter = self.counters[an_id]
|
||||||
|
if ilvl not in counter or num_id not in seen_instances:
|
||||||
|
counter[ilvl] = self.starts[num_id][ilvl]
|
||||||
|
seen_instances.add(num_id)
|
||||||
p.tag = 'li'
|
p.tag = 'li'
|
||||||
p.set('value', '%s' % counter[ilvl])
|
p.set('value', '%s' % counter[ilvl])
|
||||||
p.set('list-lvl', str(ilvl))
|
p.set('list-lvl', str(ilvl))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user