|
4 | 4 | from collections import OrderedDict |
5 | 5 | from copy import deepcopy |
6 | 6 |
|
| 7 | +from docx.enum.style import WD_STYLE_TYPE |
7 | 8 | from docx.opc.constants import CONTENT_TYPE as CT |
8 | 9 | from docx.opc.constants import RELATIONSHIP_TYPE as RT |
9 | 10 | from docx.opc.oxml import serialize_part_xml |
@@ -79,6 +80,7 @@ def insert(self, index, doc, remove_property_fields=True): |
79 | 80 | cprops.dissolve_fields(name) |
80 | 81 |
|
81 | 82 | self._create_style_id_mapping(doc) |
| 83 | + self.retain_formatting_from_default_styles(doc) |
82 | 84 |
|
83 | 85 | for element in doc.element.body: |
84 | 86 | if isinstance(element, CT_SectPr): |
@@ -299,6 +301,73 @@ def add_styles_from_other_parts(self, doc): |
299 | 301 | else: |
300 | 302 | self.add_styles(doc, el) |
301 | 303 |
|
| 304 | + def retain_formatting_from_default_styles(self, doc): |
| 305 | + """""" |
| 306 | + if not self.preserve_styles: |
| 307 | + return |
| 308 | + style_id_name_mapping = {s.style_id: s.name for s in doc.styles} |
| 309 | + for style_type in WD_STYLE_TYPE: |
| 310 | + # Currently we only support retaining paragraph styles |
| 311 | + if style_type != WD_STYLE_TYPE.PARAGRAPH: |
| 312 | + continue |
| 313 | + style = doc.styles.default(style_type) |
| 314 | + if style is not None: |
| 315 | + our_style = self.doc.styles.default(style_type) |
| 316 | + if not xml_elements_equal(our_style.element, style.element): |
| 317 | + if style_type == WD_STYLE_TYPE.PARAGRAPH: |
| 318 | + # Get formattings from the style's run properties |
| 319 | + run_properties = xpath(style.element, ".//w:rPr/*") |
| 320 | + if run_properties: |
| 321 | + # If the run doesn't have run propertes (<w:rPr>), |
| 322 | + # we need to add them |
| 323 | + for el in xpath(doc.element, ".//w:r[not(w:rPr)]"): |
| 324 | + r_pr_element = parse_xml( |
| 325 | + '<w:rPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"/>' |
| 326 | + ) |
| 327 | + el.insert(0, r_pr_element) |
| 328 | + # For every run property without a style, we add the |
| 329 | + # formattings from the default style. |
| 330 | + for el in xpath(doc.element, ".//w:rPr[not(w:rStyle)]"): |
| 331 | + # Figure out formattings defined in a style |
| 332 | + # from a parent element as we should not add these. |
| 333 | + parent = el.getparent() |
| 334 | + while parent is not None: |
| 335 | + style_ids = xpath(parent, "*/w:pStyle/@w:val") |
| 336 | + if style_ids: |
| 337 | + break |
| 338 | + parent = parent.getparent() |
| 339 | + formattings_from_style = [] |
| 340 | + for style_id in style_ids: |
| 341 | + thestyle = doc.styles[ |
| 342 | + style_id_name_mapping[style_id] |
| 343 | + ] |
| 344 | + formattings_from_style = xpath( |
| 345 | + thestyle.element, "w:rPr/*|w:pPr/*" |
| 346 | + ) |
| 347 | + for run_property in run_properties: |
| 348 | + if not any( |
| 349 | + [ |
| 350 | + f.tag == run_property.tag |
| 351 | + for f in formattings_from_style |
| 352 | + ] |
| 353 | + ): |
| 354 | + el.append(deepcopy(run_property)) |
| 355 | + # Get formattings from the style's paragraph properties |
| 356 | + paragraph_properties = xpath(style.element, ".//w:pPr/*") |
| 357 | + if paragraph_properties: |
| 358 | + # If the paragraph doesn't have paragraph propertes |
| 359 | + # (<w:pPr>), we need to add them |
| 360 | + for el in xpath(doc.element, ".//w:p[not(w:pPr)]"): |
| 361 | + p_pr_element = parse_xml( |
| 362 | + '<w:pPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"/>' |
| 363 | + ) |
| 364 | + el.insert(0, p_pr_element) |
| 365 | + # For every paragraph property without a style, we add the |
| 366 | + # formattings from the default style. |
| 367 | + for el in xpath(doc.element, ".//w:pPr[not(w:pStyle)]"): |
| 368 | + for paragraph_property in paragraph_properties: |
| 369 | + el.append(deepcopy(paragraph_property)) |
| 370 | + |
302 | 371 | def add_styles(self, doc, element): |
303 | 372 | """Add styles from the given document used in the given element.""" |
304 | 373 | our_style_ids = [s.style_id for s in self.doc.styles] |
|
0 commit comments