Skip to content

Commit

Permalink
Merge pull request #225 from botzill/fix-list-items-processing
Browse files Browse the repository at this point in the history
Fixed item listing, moved margin-left to <li>
  • Loading branch information
winhamwr authored Feb 17, 2017
2 parents 92b151e + 3ae50cb commit 98c6aa6
Show file tree
Hide file tree
Showing 19 changed files with 1,528 additions and 129 deletions.
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

0 comments on commit 98c6aa6

Please sign in to comment.