mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
DOCX Output: Start work on lists
This commit is contained in:
parent
63a2f3c21e
commit
627981f349
@ -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:
|
||||||
|
86
src/calibre/ebooks/docx/writer/lists.py
Normal file
86
src/calibre/ebooks/docx/writer/lists.py
Normal 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))
|
Loading…
x
Reference in New Issue
Block a user