diff options
| author | Michael Williamson <mike@zwobble.org> | 2020-04-12 14:58:34 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2020-04-12 14:58:34 +0100 |
| commit | 7a9d6f05dbf51db840ab010172f4ed3d6fca6257 (patch) | |
| tree | 85560bad7fddfd99a09a47932f85f0b42241dbae /mammoth/docx | |
| parent | 6f7852e45a08c60bdb5961c7a43b1b5bc41d3b10 (diff) | |
Read font size for runs
Diffstat (limited to 'mammoth/docx')
| -rw-r--r-- | mammoth/docx/body_xml.py | 168 |
1 files changed, 88 insertions, 80 deletions
diff --git a/mammoth/docx/body_xml.py b/mammoth/docx/body_xml.py index d6ca993..7bb8628 100644 --- a/mammoth/docx/body_xml.py +++ b/mammoth/docx/body_xml.py @@ -22,10 +22,10 @@ def reader( docx_file=None, files=None ): - + if styles is None: styles = Styles.EMPTY - + read_all = _create_reader( numbering=numbering, content_types=content_types, @@ -41,7 +41,7 @@ def reader( class _BodyReader(object): def __init__(self, read_all): self._read_all = read_all - + def read_all(self, elements): result = self._read_all(elements) return results.Result(result.elements, result.messages) @@ -79,20 +79,27 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f .find_child_or_null("w:vertAlign") \ .attributes.get("w:val") font = properties.find_child_or_null("w:rFonts").attributes.get("w:ascii") - + + font_size_string = properties.find_child_or_null("w:sz").attributes.get("w:val") + if font_size_string: + # w:sz gives the font size in half points, so halve the value to get the size in points + font_size = int(font_size_string) / 2 + else: + font_size = None + is_bold = read_boolean_element(properties.find_child("w:b")) is_italic = read_boolean_element(properties.find_child("w:i")) is_underline = read_boolean_element(properties.find_child("w:u")) is_strikethrough = read_boolean_element(properties.find_child("w:strike")) is_small_caps = read_boolean_element(properties.find_child("w:smallCaps")) - + def add_complex_field_hyperlink(children): hyperlink_href = current_hyperlink_href() if hyperlink_href is None: return children else: return [documents.hyperlink(href=hyperlink_href, children=children)] - + return _ReadResult.map_results( _read_run_style(properties), _read_xml_elements(element.children).map(add_complex_field_hyperlink), @@ -107,8 +114,9 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f is_small_caps=is_small_caps, vertical_alignment=vertical_alignment, font=font, + font_size=font_size, )) - + def _read_run_style(properties): return _read_style(properties, "w:rStyle", "Run", styles.find_character_style_by_id) @@ -121,7 +129,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f alignment = properties.find_child_or_null("w:jc").attributes.get("w:val") numbering = _read_numbering_properties(properties.find_child_or_null("w:numPr")) indent = _read_paragraph_indent(properties.find_child_or_null("w:ind")) - + return _ReadResult.map_results( _read_paragraph_style(properties), _read_xml_elements(element.children), @@ -133,20 +141,20 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f alignment=alignment, indent=indent, )).append_extra() - + def _read_paragraph_style(properties): return _read_style(properties, "w:pStyle", "Paragraph", styles.find_paragraph_style_by_id) - + current_instr_text = [] complex_field_stack = [] - + def current_hyperlink_href(): for complex_field in reversed(complex_field_stack): if isinstance(complex_field, complex_fields.Hyperlink): return complex_field.href return None - + def read_fld_char(element): fld_char_type = element.attributes.get("w:fldCharType") if fld_char_type == "begin": @@ -164,24 +172,24 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f complex_field_stack.pop() complex_field_stack.append(complex_field) return _empty_result - + def parse_hyperlink_field_code(instr_text): result = re.match(r'\s*HYPERLINK "(.*)"', instr_text) if result is None: return None else: return result.group(1) - + def read_instr_text(element): current_instr_text.append(_inner_text(element)) return _empty_result - + def _read_style(properties, style_tag_name, style_type, find_style_by_id): messages = [] style_id = properties \ .find_child_or_null(style_tag_name) \ .attributes.get("w:val") - + if style_id is None: style_name = None else: @@ -191,9 +199,9 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f messages.append(_undefined_style_warning(style_type, style_id)) else: style_name = style.name - + return _ReadResult([style_id, style_name], [], messages) - + def _undefined_style_warning(style_type, style_id): return results.warning("{0} style with ID {1} was referenced but not defined in the document".format(style_type, style_id)) @@ -216,31 +224,31 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f def tab(element): return _success(documents.tab()) - - + + def no_break_hyphen(element): return _success(documents.text(unichr(0x2011))) - - + + def table(element): properties = element.find_child_or_null("w:tblPr") return _ReadResult.map_results( read_table_style(properties), _read_xml_elements(element.children) .flat_map(calculate_row_spans), - + lambda style, children: documents.table( children=children, style_id=style[0], style_name=style[1], ), ) - - + + def read_table_style(properties): return _read_style(properties, "w:tblStyle", "Table", styles.find_table_style_by_id) - - + + def table_row(element): properties = element.find_child_or_null("w:trPr") is_header = bool(properties.find_child("w:tblHeader")) @@ -249,19 +257,19 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f children=children, is_header=is_header, )) - - + + def table_cell(element): properties = element.find_child_or_null("w:tcPr") gridspan = properties \ .find_child_or_null("w:gridSpan") \ .attributes.get("w:val") - + if gridspan is None: colspan = 1 else: colspan = int(gridspan) - + return _read_xml_elements(element.children) \ .map(lambda children: _add_attrs( documents.table_cell( @@ -270,7 +278,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f ), _vmerge=read_vmerge(properties), )) - + def read_vmerge(properties): vmerge_element = properties.find_child("w:vMerge") if vmerge_element is None: @@ -278,8 +286,8 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f else: val = vmerge_element.attributes.get("w:val") return val == "continue" or not val - - + + def calculate_row_spans(rows): unexpected_non_rows = any( not isinstance(row, documents.TableRow) @@ -289,7 +297,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return _elements_result_with_messages(rows, [results.warning( "unexpected non-row element in table, cell merging may be incorrect" )]) - + unexpected_non_cells = any( not isinstance(cell, documents.TableCell) for row in rows @@ -299,7 +307,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return _elements_result_with_messages(rows, [results.warning( "unexpected non-cell element in table row, cell merging may be incorrect" )]) - + columns = {} for row in rows: cell_index = 0 @@ -310,41 +318,41 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f columns[cell_index] = cell cell._vmerge = False cell_index += cell.colspan - + for row in rows: row.children = lists.filter(lambda cell: not cell._vmerge, row.children) for cell in row.children: del cell._vmerge - + return _success(rows) - - + + def read_child_elements(element): return _read_xml_elements(element.children) - - + + def pict(element): return read_child_elements(element).to_extra() - - + + def hyperlink(element): relationship_id = element.attributes.get("r:id") anchor = element.attributes.get("w:anchor") target_frame = element.attributes.get("w:tgtFrame") or None children_result = _read_xml_elements(element.children) - + def create(**kwargs): return children_result.map(lambda children: documents.hyperlink( children=children, target_frame=target_frame, **kwargs )) - + if relationship_id is not None: href = relationships.find_target_by_relationship_id(relationship_id) if anchor is not None: href = replace_fragment(href, anchor) - + return create(href=href) elif anchor is not None: return create(anchor=anchor) @@ -358,8 +366,8 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return _empty_result else: return _success(documents.bookmark(name)) - - + + def break_(element): break_type = element.attributes.get("w:type") @@ -373,7 +381,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f warning = results.warning("Unsupported break type: {0}".format(break_type)) return _empty_result_with_message(warning) - + def inline(element): properties = element.find_child_or_null("wp:docPr").attributes if properties.get("descr", "").strip(): @@ -386,25 +394,25 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f .find_children("pic:blipFill") \ .find_children("a:blip") return _read_blips(blips, alt_text) - + def _read_blips(blips, alt_text): return _ReadResult.concat(lists.map(lambda blip: _read_blip(blip, alt_text), blips)) - + def _read_blip(element, alt_text): return _read_image(lambda: _find_blip_image(element), alt_text) - + def _read_image(find_image, alt_text): image_path, open_image = find_image() content_type = content_types.find_content_type(image_path) image = documents.image(alt_text=alt_text, content_type=content_type, open=open_image) - + if content_type in ["image/png", "image/gif", "image/jpeg", "image/svg+xml", "image/tiff"]: messages = [] else: messages = [results.warning("Image of type {0} is unlikely to display in web browsers".format(content_type))] - + return _element_result_with_messages(image, messages) - + def _find_blip_image(element): embed_relationship_id = element.attributes.get("r:embed") link_relationship_id = element.attributes.get("r:link") @@ -412,29 +420,29 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return _find_embedded_image(embed_relationship_id) elif link_relationship_id is not None: return _find_linked_image(link_relationship_id) - + def _find_embedded_image(relationship_id): target = relationships.find_target_by_relationship_id(relationship_id) image_path = uri_to_zip_entry_name("word", target) - + def open_image(): image_file = docx_file.open(image_path) if hasattr(image_file, "__exit__"): return image_file else: return contextlib.closing(image_file) - + return image_path, open_image - - + + def _find_linked_image(relationship_id): image_path = relationships.find_target_by_relationship_id(relationship_id) - + def open_image(): return files.open(image_path) - + return image_path, open_image - + def read_imagedata(element): relationship_id = element.attributes.get("r:id") if relationship_id is None: @@ -443,19 +451,19 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f else: title = element.attributes.get("o:title") return _read_image(lambda: _find_embedded_image(relationship_id), title) - + def note_reference_reader(note_type): def note_reference(element): return _success(documents.note_reference(note_type, element.attributes["w:id"])) - + return note_reference - + def read_comment_reference(element): return _success(documents.comment_reference(element.attributes["w:id"])) def alternate_content(element): return read_child_elements(element.find_child("mc:Fallback")) - + def read_sdt(element): return read_child_elements(element.find_child_or_null("w:sdtContent")) @@ -493,7 +501,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f "mc:AlternateContent": alternate_content, "w:sdt": read_sdt } - + def read(element): handler = handlers.get(element.name) if handler is None: @@ -504,12 +512,12 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return _empty_result else: return handler(element) - + def _read_xml_elements(nodes): elements = filter(lambda node: isinstance(node, XmlElement), nodes) return _ReadResult.concat(lists.map(read, elements)) - + return _read_xml_elements @@ -528,20 +536,20 @@ class _ReadResult(object): lists.flat_map(lambda result: result.elements, results), lists.flat_map(lambda result: result.extra, results), lists.flat_map(lambda result: result.messages, results)) - - + + @staticmethod def map_results(first, second, func): return _ReadResult( [func(first.elements, second.elements)], first.extra + second.extra, first.messages + second.messages) - + def __init__(self, elements, extra, messages): self.elements = elements self.extra = extra self.messages = messages - + def map(self, func): elements = func(self.elements) if not isinstance(elements, list): @@ -550,18 +558,18 @@ class _ReadResult(object): elements, self.extra, self.messages) - + def flat_map(self, func): result = func(self.elements) return _ReadResult( result.elements, self.extra + result.extra, self.messages + result.messages) - - + + def to_extra(self): return _ReadResult([], _concat(self.extra, self.elements), self.messages) - + def append_extra(self): return _ReadResult(_concat(self.elements, self.extra), [], self.messages) @@ -592,5 +600,5 @@ def _concat(*values): def _add_attrs(obj, **kwargs): for key, value in kwargs.items(): setattr(obj, key, value) - + return obj |
