From 20de4545925d8587d21eda7bbafb7c2bb715f388 Mon Sep 17 00:00:00 2001 From: Alexander Parrill Date: Thu, 12 Sep 2024 14:52:16 -0400 Subject: [PATCH 1/2] Fix blockquotes and list nesting throwing an error Combining the two still produces ugly results, since in Word a paragraph cannot have a "List Paragraph" and "Blockquote" style at the same time. However it no longer throws an error. Fixes #516 --- ghostwriter/modules/reportwriter/richtext/docx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghostwriter/modules/reportwriter/richtext/docx.py b/ghostwriter/modules/reportwriter/richtext/docx.py index 1cc40b1ec..f49c5ec5a 100644 --- a/ghostwriter/modules/reportwriter/richtext/docx.py +++ b/ghostwriter/modules/reportwriter/richtext/docx.py @@ -209,7 +209,7 @@ def tag_ul(self, el, *, par=None, list_level=None, list_tracking=None, **kwargs) tag_ol = tag_ul - def tag_blockquote(self, el, **kwargs): + def tag_blockquote(self, el, par=None, **kwargs): # TODO: if done in a list, this won't preserve the level. # Not sure how to do that, since this requires a new paragraph. par = self.doc.add_paragraph() From ee3b1e3457bffdfbc071bd01d30a64a0ffc72ed4 Mon Sep 17 00:00:00 2001 From: Alexander Parrill Date: Thu, 12 Sep 2024 15:31:45 -0400 Subject: [PATCH 2/2] Fix error for documents without ListParagraph style Also adds checking in a few other places where styles are assigned. In my experience, deleting the list paragraph style will also delete the numberings XML document entirely, which python-docx cannot recreate. This patch also checks for that and shows the user a more friendly error message. Fixes #499 --- .../modules/reportwriter/richtext/docx.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/ghostwriter/modules/reportwriter/richtext/docx.py b/ghostwriter/modules/reportwriter/richtext/docx.py index f49c5ec5a..e68ace887 100644 --- a/ghostwriter/modules/reportwriter/richtext/docx.py +++ b/ghostwriter/modules/reportwriter/richtext/docx.py @@ -18,6 +18,7 @@ from lxml import etree # Ghostwriter Libraries +from ghostwriter.modules.reportwriter.base import ReportExportError from ghostwriter.modules.reportwriter.extensions import ( IMAGE_EXTENSIONS, TEXT_EXTENSIONS, @@ -69,7 +70,10 @@ def text(self, el, *, par=None, style={}, **kwargs): run._r.append(hyperlink) # A workaround for the lack of a hyperlink style if "Hyperlink" in self.doc.styles: - run.style = "Hyperlink" + try: + run.style = "Hyperlink" + except KeyError: + pass else: run.font.color.theme_color = MSO_THEME_COLOR_INDEX.HYPERLINK run.font.underline = True @@ -79,7 +83,10 @@ def text(self, el, *, par=None, style={}, **kwargs): def style_run(self, run, style): super().style_run(run, style) if style.get("inline_code"): - run.style = "CodeInline" + try: + run.style = "CodeInline" + except KeyError: + pass run.font.no_proof = True if style.get("highlight"): run.font.highlight_color = WD_COLOR_INDEX.YELLOW @@ -296,7 +303,10 @@ def tag_span(self, el, *, par, **kwargs): self.make_evidence(par, evidence) elif "data-gw-caption" in el.attrs: ref_name = el.attrs["data-gw-caption"] - par.style = "Caption" + try: + par.style = "Caption" + except KeyError: + pass par._gw_is_caption = True self.make_figure(par, ref_name or None) elif "data-gw-ref" in el.attrs: @@ -523,7 +533,10 @@ def create(self, doc): level_list_is_ordered = self.level_list_is_ordered # Create a new numbering - numbering = doc.part.numbering_part.numbering_definitions._numbering + try: + numbering = doc.part.numbering_part.numbering_definitions._numbering + except NotImplementedError as e: + raise ReportExportError("Tried to use a list in a template without list styles") from e last_used_id = max( (int(id) for id in numbering.xpath("w:abstractNum/@w:abstractNumId")), default=-1, @@ -580,6 +593,9 @@ def create(self, doc): numbering_id = numbering.add_num(abstract_numbering_id).numId for par, level in self.paragraphs: - par.style = "ListParagraph" + try: + par.style = "ListParagraph" + except KeyError: + pass par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_numId().val = numbering_id par._p.get_or_add_pPr().get_or_add_numPr().get_or_add_ilvl().val = level