Skip to content

Commit 3ad14a5

Browse files
committed
Preserve styles WiP
1 parent 199da90 commit 3ad14a5

4 files changed

Lines changed: 47 additions & 14 deletions

File tree

docxcompose/composer.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from docxcompose.image import ImageWrapper
1717
from docxcompose.properties import CustomProperties
18+
from docxcompose.utils import increment_name
1819
from docxcompose.utils import NS
1920
from docxcompose.utils import xpath
2021

@@ -41,6 +42,7 @@ def __init__(self, doc):
4142
self.pkg = doc.part.package
4243

4344
self.restart_numbering = True
45+
self.preserve_styles = True
4446

4547
self.reset_reference_mapping()
4648

@@ -59,6 +61,7 @@ def append(self, doc, remove_property_fields=True):
5961
def insert(self, index, doc, remove_property_fields=True):
6062
"""Insert the given document at the given index."""
6163
self.reset_reference_mapping()
64+
self._preserved_styles = {}
6265

6366
# Remove custom property fields but keep the values
6467
if remove_property_fields:
@@ -299,24 +302,30 @@ def add_styles(self, doc, element):
299302

300303
for style_id in used_style_ids:
301304
our_style_id = self.mapped_style_id(style_id)
302-
if our_style_id not in our_style_ids:
305+
if self.preserve_styles and our_style_id in our_style_ids:
306+
if our_style_id not in self._preserved_styles:
307+
style_element = deepcopy(doc.styles.element.get_by_id(style_id))
308+
our_style_element = self.doc.styles.element.get_by_id(our_style_id)
309+
if style_element.xml != our_style_element.xml:
310+
new_id = increment_name(our_style_id)
311+
new_name = increment_name(style_element.name.val)
312+
while new_id in our_style_ids:
313+
new_id = increment_name(new_id)
314+
new_name = increment_name(new_name)
315+
style_element.styleId = new_id
316+
style_element.name.val = new_name
317+
self.doc.styles.element.append(style_element)
318+
self.add_numberings(doc, style_element)
319+
self.add_linked_styles(doc, style_element)
320+
self._preserved_styles[our_style_id] = style_element.styleId
321+
for el in xpath(element, ".//w:tblStyle|.//w:pStyle|.//w:rStyle"):
322+
el.val = self._preserved_styles[our_style_id]
323+
elif our_style_id not in our_style_ids:
303324
style_element = deepcopy(doc.styles.element.get_by_id(style_id))
304325
if style_element is not None:
305326
self.doc.styles.element.append(style_element)
306327
self.add_numberings(doc, style_element)
307-
# Also add linked styles
308-
linked_style_ids = xpath(style_element, ".//w:link/@w:val")
309-
if linked_style_ids:
310-
linked_style_id = linked_style_ids[0]
311-
our_linked_style_id = self.mapped_style_id(linked_style_id)
312-
if our_linked_style_id not in our_style_ids:
313-
our_linked_style = doc.styles.element.get_by_id(
314-
linked_style_id
315-
)
316-
if our_linked_style is not None:
317-
self.doc.styles.element.append(
318-
deepcopy(our_linked_style)
319-
)
328+
self.add_linked_styles(doc, style_element)
320329
else:
321330
# Create a mapping for abstractNumIds used in existing styles
322331
# This is used when adding numberings to avoid having multiple
@@ -360,6 +369,21 @@ def add_styles(self, doc, element):
360369
# Update our style ids
361370
our_style_ids = [s.style_id for s in self.doc.styles]
362371

372+
def add_linked_styles(self, doc, element):
373+
linked_style_ids = xpath(element, ".//w:link/@w:val")
374+
if linked_style_ids:
375+
linked_style_id = linked_style_ids[0]
376+
our_linked_style_id = self.mapped_style_id(linked_style_id)
377+
our_style_ids = [s.style_id for s in self.doc.styles]
378+
if our_linked_style_id not in our_style_ids:
379+
our_linked_style = doc.styles.element.get_by_id(
380+
linked_style_id
381+
)
382+
if our_linked_style is not None:
383+
self.doc.styles.element.append(
384+
deepcopy(our_linked_style)
385+
)
386+
363387
def add_numberings(self, doc, element):
364388
"""Add numberings from the given document used in the given element."""
365389
# Search for numbering references

docxcompose/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ def word_to_python_date_format(format_str):
4848
for word_format, python_format in date_format_map:
4949
format_str = re.sub(word_format, python_format, format_str)
5050
return format_str
51+
52+
53+
def increment_name(name):
54+
increment_part = name.split('_')[-1]
55+
try:
56+
increment = int(increment_part)
57+
except ValueError:
58+
return f'{name}_1'
59+
return f'{name.removesuffix(increment_part)}{increment + 1}'

tests/docs/styles_preserve1.docx

15.9 KB
Binary file not shown.

tests/docs/styles_preserve2.docx

12.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)