Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pydocx/export/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def yield_numbering_spans(self, items):
for item in items:
yield item
return
builder = self.numbering_span_builder_class(items)
builder = self.numbering_span_builder_class(items, process_components=True)
numbering_spans = builder.get_numbering_spans()
for item in numbering_spans:
yield item
Expand Down
182 changes: 143 additions & 39 deletions pydocx/export/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
POINTS_PER_EM,
PYDOCX_STYLES,
TWIPS_PER_POINT,
EMUS_PER_PIXEL,
EMUS_PER_PIXEL
)
from pydocx.export.base import PyDocXExporter
from pydocx.export.numbering_span import NumberingItem
Expand Down Expand Up @@ -96,12 +96,12 @@ class HtmlTag(object):
closed_tag_format = '</{tag}>'

def __init__(
self,
tag,
allow_self_closing=False,
closed=False,
allow_whitespace=False,
**attrs
self,
tag,
allow_self_closing=False,
closed=False,
allow_whitespace=False,
**attrs
):
self.tag = tag
self.allow_self_closing = allow_self_closing
Expand Down Expand Up @@ -311,46 +311,45 @@ def export_paragraph_property_justification(self, paragraph, results):
def export_paragraph_property_indentation(self, paragraph, results):
# TODO these classes should be applied on the paragraph, and not as
# inline styles

properties = paragraph.effective_properties

style = {}

if properties.indentation_right:
# TODO would be nice if this integer conversion was handled
# implicitly by the model somehow
try:
right = int(properties.indentation_right)
except ValueError:
right = None
# Numbering properties can define a text indentation on a paragraph
if properties.numbering_properties:
indentation_left = None
indentation_first_line = None

if right:
right = convert_twips_to_ems(right)
style['margin-right'] = '{0:.2f}em'.format(right)
paragraph_num_level = paragraph.get_numbering_level()

if properties.indentation_left:
# TODO would be nice if this integer conversion was handled
# implicitly by the model somehow
try:
left = int(properties.indentation_left)
except ValueError:
left = None
if paragraph_num_level:
listing_style = self.export_listing_paragraph_property_indentation(
paragraph,
paragraph_num_level.paragraph_properties,
include_text_indent=True
)
if 'text-indent' in listing_style and listing_style['text-indent'] != '0.00em':
style['text-indent'] = listing_style['text-indent']
style['display'] = 'inline-block'
else:
indentation_left = properties.to_int('indentation_left')
indentation_first_line = properties.to_int('indentation_first_line')

if left:
left = convert_twips_to_ems(left)
style['margin-left'] = '{0:.2f}em'.format(left)
indentation_right = properties.to_int('indentation_right')

if properties.indentation_first_line:
# TODO would be nice if this integer conversion was handled
# implicitly by the model somehow
try:
first_line = int(properties.indentation_first_line)
except ValueError:
first_line = None
if indentation_right:
right = convert_twips_to_ems(indentation_right)
style['margin-right'] = '{0:.2f}em'.format(right)

if indentation_left:
left = convert_twips_to_ems(indentation_left)
style['margin-left'] = '{0:.2f}em'.format(left)

if first_line:
first_line = convert_twips_to_ems(first_line)
# TODO text-indent doesn't work with inline elements like span
style['text-indent'] = '{0:.2f}em'.format(first_line)
if indentation_first_line:
first_line = convert_twips_to_ems(indentation_first_line)
style['text-indent'] = '{0:.2f}em'.format(first_line)
style['display'] = 'inline-block'

if style:
attrs = {
Expand All @@ -361,6 +360,93 @@ def export_paragraph_property_indentation(self, paragraph, results):

return results

def export_listing_paragraph_property_indentation(
self,
paragraph,
level_properties,
include_text_indent=False
):
style = {}

if not level_properties or not paragraph.has_numbering_properties:
return style

level_indentation_step = \
paragraph.numbering_definition.get_indentation_between_levels()

paragraph_properties = paragraph.properties

level_ind_left = level_properties.to_int('indentation_left', default=0)
level_ind_hanging = level_properties.to_int('indentation_hanging', default=0)

paragraph_ind_left = paragraph_properties.to_int('indentation_left', default=0)
paragraph_ind_hanging = paragraph_properties.to_int('indentation_hanging', default=0)
paragraph_ind_first_line = paragraph_properties.to_int('indentation_first_line',
default=0)

left = paragraph_ind_left or level_ind_left
hanging = paragraph_ind_hanging or level_ind_hanging
# At this point we have no info about indentation, so we keep the default one
if not left and not hanging:
return style

# All the bellow left margin calculation is done because html ul/ol/li elements have
# their default indentations and we need to make sure that we migrate as near as
# possible solution to html.
margin_left = left

# Because hanging can be set independently, we remove it from left margin and will
# be added as text-indent later on
margin_left -= hanging

# Take into account that current span can have custom left margin
if level_indentation_step > level_ind_hanging:
margin_left -= (level_indentation_step - level_ind_hanging)
else:
margin_left -= level_indentation_step

# First line are added to left margins
margin_left += paragraph_ind_first_line

if isinstance(paragraph.parent, NumberingItem):
try:
# In case of nested lists elements, we need to adjust left margin
# based on the parent item
parent_paragraph = paragraph.parent.numbering_span.parent.get_first_child()

parent_ind_left = parent_paragraph.get_indentation('indentation_left')
parent_ind_hanging = parent_paragraph.get_indentation('indentation_hanging')
parent_lvl_ind_hanging = parent_paragraph.get_indentation(
'indentation_hanging')

margin_left -= (parent_ind_left - parent_ind_hanging)
margin_left -= parent_lvl_ind_hanging
# To mimic the word way of setting first line, we need to move back(left) all
# elements by first_line value
margin_left -= parent_paragraph.get_indentation('indentation_first_line')
except AttributeError:
pass

# Here as well, we remove the default hanging which word adds
# because <li> tag will provide it's own
hanging -= level_ind_hanging

if margin_left:
margin_left = convert_twips_to_ems(margin_left)
style['margin-left'] = '{0:.2f}em'.format(margin_left)

# we don't allow negative hanging
if hanging < 0:
hanging = 0

if include_text_indent:
if hanging is not None:
# Now, here we add the hanging as text-indent for the paragraph
hanging = convert_twips_to_ems(hanging)
style['text-indent'] = '{0:.2f}em'.format(hanging)

return style

def get_run_styles_to_apply(self, run):
parent_paragraph = run.get_first_ancestor(wordprocessing.Paragraph)
if parent_paragraph and parent_paragraph.heading_style:
Expand Down Expand Up @@ -737,7 +823,25 @@ def export_numbering_item(self, numbering_item):
numbering_item.children,
self.export_node,
)
tag = HtmlTag('li')

style = None

if numbering_item.children:
level_properties = numbering_item.numbering_span.\
numbering_level.paragraph_properties
# get the first paragraph properties which will contain information
# on how to properly indent listing item
paragraph = numbering_item.children[0]

style = self.export_listing_paragraph_property_indentation(paragraph,
level_properties)

attrs = {}

if style:
attrs['style'] = convert_dictionary_to_style_fragment(style)

tag = HtmlTag('li', **attrs)
return tag.apply(results)

def export_field_hyperlink(self, simple_field, field_args):
Expand Down
Loading