1717 POINTS_PER_EM ,
1818 PYDOCX_STYLES ,
1919 TWIPS_PER_POINT ,
20- EMUS_PER_PIXEL ,
20+ EMUS_PER_PIXEL
2121)
2222from pydocx .export .base import PyDocXExporter
2323from pydocx .export .numbering_span import NumberingItem
@@ -96,12 +96,12 @@ class HtmlTag(object):
9696 closed_tag_format = '</{tag}>'
9797
9898 def __init__ (
99- self ,
100- tag ,
101- allow_self_closing = False ,
102- closed = False ,
103- allow_whitespace = False ,
104- ** attrs
99+ self ,
100+ tag ,
101+ allow_self_closing = False ,
102+ closed = False ,
103+ allow_whitespace = False ,
104+ ** attrs
105105 ):
106106 self .tag = tag
107107 self .allow_self_closing = allow_self_closing
@@ -311,46 +311,45 @@ def export_paragraph_property_justification(self, paragraph, results):
311311 def export_paragraph_property_indentation (self , paragraph , results ):
312312 # TODO these classes should be applied on the paragraph, and not as
313313 # inline styles
314+
314315 properties = paragraph .effective_properties
315316
316317 style = {}
317318
318- if properties .indentation_right :
319- # TODO would be nice if this integer conversion was handled
320- # implicitly by the model somehow
321- try :
322- right = int (properties .indentation_right )
323- except ValueError :
324- right = None
319+ # Numbering properties can define a text indentation on a paragraph
320+ if properties .numbering_properties :
321+ indentation_left = None
322+ indentation_first_line = None
325323
326- if right :
327- right = convert_twips_to_ems (right )
328- style ['margin-right' ] = '{0:.2f}em' .format (right )
324+ paragraph_num_level = paragraph .get_numbering_level ()
329325
330- if properties .indentation_left :
331- # TODO would be nice if this integer conversion was handled
332- # implicitly by the model somehow
333- try :
334- left = int (properties .indentation_left )
335- except ValueError :
336- left = None
326+ if paragraph_num_level :
327+ listing_style = self .export_listing_paragraph_property_indentation (
328+ paragraph ,
329+ paragraph_num_level .paragraph_properties ,
330+ include_text_indent = True
331+ )
332+ if 'text-indent' in listing_style and listing_style ['text-indent' ] != '0.00em' :
333+ style ['text-indent' ] = listing_style ['text-indent' ]
334+ style ['display' ] = 'inline-block'
335+ else :
336+ indentation_left = properties .to_int ('indentation_left' )
337+ indentation_first_line = properties .to_int ('indentation_first_line' )
337338
338- if left :
339- left = convert_twips_to_ems (left )
340- style ['margin-left' ] = '{0:.2f}em' .format (left )
339+ indentation_right = properties .to_int ('indentation_right' )
341340
342- if properties . indentation_first_line :
343- # TODO would be nice if this integer conversion was handled
344- # implicitly by the model somehow
345- try :
346- first_line = int ( properties . indentation_first_line )
347- except ValueError :
348- first_line = None
341+ if indentation_right :
342+ right = convert_twips_to_ems ( indentation_right )
343+ style [ 'margin-right' ] = '{0:.2f}em' . format ( right )
344+
345+ if indentation_left :
346+ left = convert_twips_to_ems ( indentation_left )
347+ style [ 'margin-left' ] = '{0:.2f}em' . format ( left )
349348
350- if first_line :
351- first_line = convert_twips_to_ems (first_line )
352- # TODO text-indent doesn't work with inline elements like span
353- style ['text-indent ' ] = '{0:.2f}em' . format ( first_line )
349+ if indentation_first_line :
350+ first_line = convert_twips_to_ems (indentation_first_line )
351+ style [ ' text-indent' ] = '{0:.2f}em' . format ( first_line )
352+ style ['display ' ] = 'inline-block'
354353
355354 if style :
356355 attrs = {
@@ -361,6 +360,93 @@ def export_paragraph_property_indentation(self, paragraph, results):
361360
362361 return results
363362
363+ def export_listing_paragraph_property_indentation (
364+ self ,
365+ paragraph ,
366+ level_properties ,
367+ include_text_indent = False
368+ ):
369+ style = {}
370+
371+ if not level_properties or not paragraph .has_numbering_properties :
372+ return style
373+
374+ level_indentation_step = \
375+ paragraph .numbering_definition .get_indentation_between_levels ()
376+
377+ paragraph_properties = paragraph .properties
378+
379+ level_ind_left = level_properties .to_int ('indentation_left' , default = 0 )
380+ level_ind_hanging = level_properties .to_int ('indentation_hanging' , default = 0 )
381+
382+ paragraph_ind_left = paragraph_properties .to_int ('indentation_left' , default = 0 )
383+ paragraph_ind_hanging = paragraph_properties .to_int ('indentation_hanging' , default = 0 )
384+ paragraph_ind_first_line = paragraph_properties .to_int ('indentation_first_line' ,
385+ default = 0 )
386+
387+ left = paragraph_ind_left or level_ind_left
388+ hanging = paragraph_ind_hanging or level_ind_hanging
389+ # At this point we have no info about indentation, so we keep the default one
390+ if not left and not hanging :
391+ return style
392+
393+ # All the bellow left margin calculation is done because html ul/ol/li elements have
394+ # their default indentations and we need to make sure that we migrate as near as
395+ # possible solution to html.
396+ margin_left = left
397+
398+ # Because hanging can be set independently, we remove it from left margin and will
399+ # be added as text-indent later on
400+ margin_left -= hanging
401+
402+ # Take into account that current span can have custom left margin
403+ if level_indentation_step > level_ind_hanging :
404+ margin_left -= (level_indentation_step - level_ind_hanging )
405+ else :
406+ margin_left -= level_indentation_step
407+
408+ # First line are added to left margins
409+ margin_left += paragraph_ind_first_line
410+
411+ if isinstance (paragraph .parent , NumberingItem ):
412+ try :
413+ # In case of nested lists elements, we need to adjust left margin
414+ # based on the parent item
415+ parent_paragraph = paragraph .parent .numbering_span .parent .get_first_child ()
416+
417+ parent_ind_left = parent_paragraph .get_indentation ('indentation_left' )
418+ parent_ind_hanging = parent_paragraph .get_indentation ('indentation_hanging' )
419+ parent_lvl_ind_hanging = parent_paragraph .get_indentation (
420+ 'indentation_hanging' )
421+
422+ margin_left -= (parent_ind_left - parent_ind_hanging )
423+ margin_left -= parent_lvl_ind_hanging
424+ # To mimic the word way of setting first line, we need to move back(left) all
425+ # elements by first_line value
426+ margin_left -= parent_paragraph .get_indentation ('indentation_first_line' )
427+ except AttributeError :
428+ pass
429+
430+ # Here as well, we remove the default hanging which word adds
431+ # because <li> tag will provide it's own
432+ hanging -= level_ind_hanging
433+
434+ if margin_left :
435+ margin_left = convert_twips_to_ems (margin_left )
436+ style ['margin-left' ] = '{0:.2f}em' .format (margin_left )
437+
438+ # we don't allow negative hanging
439+ if hanging < 0 :
440+ hanging = 0
441+
442+ if include_text_indent :
443+ if hanging is not None :
444+ # Now, here we add the hanging as text-indent for the paragraph
445+ hanging = convert_twips_to_ems (hanging )
446+ style ['text-indent' ] = '{0:.2f}em' .format (hanging )
447+
448+ return style
449+
364450 def get_run_styles_to_apply (self , run ):
365451 parent_paragraph = run .get_first_ancestor (wordprocessing .Paragraph )
366452 if parent_paragraph and parent_paragraph .heading_style :
@@ -737,7 +823,25 @@ def export_numbering_item(self, numbering_item):
737823 numbering_item .children ,
738824 self .export_node ,
739825 )
740- tag = HtmlTag ('li' )
826+
827+ style = None
828+
829+ if numbering_item .children :
830+ level_properties = numbering_item .numbering_span .\
831+ numbering_level .paragraph_properties
832+ # get the first paragraph properties which will contain information
833+ # on how to properly indent listing item
834+ paragraph = numbering_item .children [0 ]
835+
836+ style = self .export_listing_paragraph_property_indentation (paragraph ,
837+ level_properties )
838+
839+ attrs = {}
840+
841+ if style :
842+ attrs ['style' ] = convert_dictionary_to_style_fragment (style )
843+
844+ tag = HtmlTag ('li' , ** attrs )
741845 return tag .apply (results )
742846
743847 def export_field_hyperlink (self , simple_field , field_args ):
0 commit comments