DOCX Output: Start work on lists

This commit is contained in:
Kovid Goyal 2015-04-24 08:07:47 +05:30
parent 63a2f3c21e
commit 627981f349
2 changed files with 100 additions and 7 deletions

View File

@ -13,6 +13,7 @@ from calibre.ebooks.docx.writer.styles import StylesManager, FloatSpec
from calibre.ebooks.docx.writer.images import ImagesManager from calibre.ebooks.docx.writer.images import ImagesManager
from calibre.ebooks.docx.writer.fonts import FontsManager from calibre.ebooks.docx.writer.fonts import FontsManager
from calibre.ebooks.docx.writer.tables import Table from calibre.ebooks.docx.writer.tables import Table
from calibre.ebooks.docx.writer.lists import ListsManager
from calibre.ebooks.oeb.stylizer import Stylizer as Sz, Style as St from calibre.ebooks.oeb.stylizer import Stylizer as Sz, Style as St
from calibre.ebooks.oeb.base import XPath, barename from calibre.ebooks.oeb.base import XPath, barename
@ -96,8 +97,9 @@ class TextRun(object):
class Block(object): class Block(object):
def __init__(self, namespace, styles_manager, html_block, style, is_table_cell=False, float_spec=None): def __init__(self, namespace, styles_manager, html_block, style, is_table_cell=False, float_spec=None, is_list_item=False):
self.namespace = namespace self.namespace = namespace
self.list_tag = (html_block, style) if is_list_item else None
self.parent_items = None self.parent_items = None
self.html_block = html_block self.html_block = html_block
self.float_spec = float_spec self.float_spec = float_spec
@ -116,6 +118,8 @@ class Block(object):
return return
if len(self.html_block) > 0 and self.html_block[0] is next_block.html_block: if len(self.html_block) > 0 and self.html_block[0] is next_block.html_block:
self.skipped = True self.skipped = True
if self.list_tag is not None:
next_block.list_tag = self.list_tag
def add_text(self, text, style, ignore_leading_whitespace=False, html_parent=None, is_parent_style=False): def add_text(self, text, style, ignore_leading_whitespace=False, html_parent=None, is_parent_style=False):
ts = self.styles_manager.create_text_style(style, is_parent_style=is_parent_style) ts = self.styles_manager.create_text_style(style, is_parent_style=is_parent_style)
@ -201,9 +205,11 @@ class Blocks(object):
self.current_block.parent_items = self.items self.current_block.parent_items = self.items
self.current_block = None self.current_block = None
def start_new_block(self, html_block, style, is_table_cell=False, float_spec=None): def start_new_block(self, html_block, style, is_table_cell=False, float_spec=None, is_list_item=False):
self.end_current_block() self.end_current_block()
self.current_block = Block(self.namespace, self.styles_manager, html_block, style, is_table_cell=is_table_cell, float_spec=float_spec) self.current_block = Block(
self.namespace, self.styles_manager, html_block, style,
is_table_cell=is_table_cell, float_spec=float_spec, is_list_item=is_list_item)
self.open_html_blocks.add(html_block) self.open_html_blocks.add(html_block)
return self.current_block return self.current_block
@ -286,6 +292,7 @@ class Convert(object):
self.styles_manager = StylesManager(self.docx.namespace) self.styles_manager = StylesManager(self.docx.namespace)
self.images_manager = ImagesManager(self.oeb, self.docx.document_relationships) self.images_manager = ImagesManager(self.oeb, self.docx.document_relationships)
self.lists_manager = ListsManager(self.docx)
self.fonts_manager = FontsManager(self.docx.namespace, self.oeb, self.opts) self.fonts_manager = FontsManager(self.docx.namespace, self.oeb, self.opts)
self.blocks = Blocks(self.docx.namespace, self.styles_manager) self.blocks = Blocks(self.docx.namespace, self.styles_manager)
@ -305,6 +312,7 @@ class Convert(object):
for pos, block in reversed(remove_blocks): for pos, block in reversed(remove_blocks):
self.blocks.delete_block_at(pos) self.blocks.delete_block_at(pos)
self.lists_manager.finalize(all_blocks)
self.styles_manager.finalize(all_blocks) self.styles_manager.finalize(all_blocks)
self.write() self.write()
@ -338,8 +346,7 @@ class Convert(object):
else: else:
self.add_inline_tag(tagname, html_tag, tag_style, stylizer) self.add_inline_tag(tagname, html_tag, tag_style, stylizer)
elif display == 'list-item': elif display == 'list-item':
# TODO: Implement this self.add_block_tag(tagname, html_tag, tag_style, stylizer, is_list_item=True)
self.add_block_tag(tagname, html_tag, tag_style, stylizer)
elif display.startswith('table') or display == 'inline-table': elif display.startswith('table') or display == 'inline-table':
if display == 'table-cell': if display == 'table-cell':
self.blocks.start_new_cell(html_tag, tag_style) self.blocks.start_new_cell(html_tag, tag_style)
@ -374,8 +381,8 @@ class Convert(object):
block = self.blocks.current_or_new_block(html_tag.getparent(), stylizer.style(html_tag.getparent())) block = self.blocks.current_or_new_block(html_tag.getparent(), stylizer.style(html_tag.getparent()))
block.add_text(html_tag.tail, stylizer.style(html_tag.getparent()), is_parent_style=True) block.add_text(html_tag.tail, stylizer.style(html_tag.getparent()), is_parent_style=True)
def add_block_tag(self, tagname, html_tag, tag_style, stylizer, is_table_cell=False, float_spec=None): def add_block_tag(self, tagname, html_tag, tag_style, stylizer, is_table_cell=False, float_spec=None, is_list_item=False):
block = self.blocks.start_new_block(html_tag, tag_style, is_table_cell=is_table_cell, float_spec=float_spec) block = self.blocks.start_new_block(html_tag, tag_style, is_table_cell=is_table_cell, float_spec=float_spec, is_list_item=is_list_item)
if tagname == 'img': if tagname == 'img':
self.images_manager.add_image(html_tag, block, stylizer) self.images_manager.add_image(html_tag, block, stylizer)
else: else:

View File

@ -0,0 +1,86 @@
#!/usr/bin/env python2
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
from collections import defaultdict
LIST_STYLES = frozenset(
'disc circle square decimal decimal-leading-zero lower-roman upper-roman'
' lower-greek lower-alpha lower-latin upper-alpha upper-latin hiragana hebrew'
' katakana-iroha cjk-ideographic'.split())
STYLE_MAP = {
'disc': 'bullet',
'circle': 'o',
'square': '\uf0a7',
'decimal': 'decimal',
'decimal-leading-zero': 'decimalZero',
'lower-roman': 'lowerRoman',
'upper-roman': 'upperRoman',
'lower-alpha': 'lowerLetter',
'lower-latin': 'lowerLetter',
'upper-alpha': 'upperLetter',
'upper-latin': 'upperLetter',
'hiragana': 'aiueo',
'hebrew': 'hebrew1',
'katakana-iroha': 'iroha',
'cjk-ideographic': 'chineseCounting',
}
def find_list_containers(list_tag, tag_style):
node = list_tag
stylizer = tag_style.stylizer
ans = []
while True:
parent = node.getparent()
if parent is None or parent is node:
break
node = parent
style = stylizer.style(node)
lst = (style._style.get('list-style-type', None) or '').lower()
if lst in LIST_STYLES:
ans.append(node)
return ans
class NumberingDefinition(object):
def __init__(self):
pass
class Level(object):
def __init__(self, list_type, container, items, ilvl=0):
self.ilvl = ilvl
try:
self.start = int(container.get('start'))
except Exception:
self.start = 1
if list_type in {'disc', 'circle', 'square'}:
self.num_fmt = 'bullet'
self.lvl_text = '%1' if list_type == 'disc' else STYLE_MAP['list_type']
else:
self.lvl_text = '%1.'
self.num_fmt = STYLE_MAP.get(list_type, 'decimal')
class ListManager(object):
def __init__(self, docx):
self.namespace = docx.namespace
def finalize(self, all_blocks):
lists = defaultdict(list)
for block in all_blocks:
if block.list_tag is not None:
list_tag, tag_style = block.list_tag
list_type = (tag_style['list-style-type'] or '').lower()
if list_type not in LIST_STYLES:
continue
container_tags = find_list_containers(list_tag, tag_style)
if not container_tags:
continue
lists[(tuple(container_tags), list_type)].append((list_tag, tag_style))