Skip to content

Commit aa7b7a7

Browse files
committed
Rich Text Paragraph Formatting Update
1 parent b97fa32 commit aa7b7a7

File tree

6 files changed

+92
-3
lines changed

6 files changed

+92
-3
lines changed

docxtpl/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
# flake8: noqa
1010
from .inline_image import InlineImage
1111
from .listing import Listing
12-
from .richtext import RichText, R
12+
from .richtext import RichText, R, RichTextParagraph, RP
1313
from .subdoc import Subdoc
1414
from .template import DocxTemplate

docxtpl/richtext.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,50 @@ def __str__(self):
119119
def __html__(self):
120120
return self.xml
121121

122+
class RichTextParagraph(object):
123+
"""class to generate Rich Text Paragraphs when using templates variables
124+
125+
This is much faster than using Subdoc class,
126+
but this only for texts OUTSIDE an existing paragraph.
127+
"""
128+
129+
def __init__(self, text=None, **text_prop):
130+
self.xml = ""
131+
if text:
132+
self.add(text, **text_prop)
133+
134+
def add(
135+
self,
136+
text,
137+
parastyle=None,
138+
):
139+
140+
# If a RichText is added
141+
if not isinstance(text, RichText):
142+
text = RichText(text)
143+
144+
prop = ""
145+
if parastyle:
146+
prop += '<w:pStyle w:val="%s"/>' % parastyle
147+
148+
xml = "<w:p>"
149+
if prop:
150+
xml += "<w:pPr>%s</w:pPr>" % prop
151+
xml += text.xml
152+
xml += "</w:p>"
153+
self.xml += xml
154+
155+
def __unicode__(self):
156+
return self.xml
157+
158+
def __str__(self):
159+
return self.xml
160+
161+
def __html__(self):
162+
return self.xml
163+
164+
165+
122166

123167
R = RichText
168+
RP = RichTextParagraph

docxtpl/template.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def cellbg(m):
160160
flags=re.DOTALL,
161161
)
162162
src_xml = re.sub(
163-
r"({{r\s.*?}}|{%r\s.*?%})",
163+
r"({{r.\s.*?}}|{%r.\s.*?%})",
164164
r'</w:t></w:r><w:r><w:t xml:space="preserve">\1</w:t></w:r><w:r><w:t xml:space="preserve">',
165165
src_xml,
166166
flags=re.DOTALL,
@@ -173,7 +173,7 @@ def cellbg(m):
173173
r"-%}(?:(?!<w:t[ >]|{%|{{).)*?<w:t[^>]*?>", "%}", src_xml, flags=re.DOTALL
174174
)
175175

176-
for y in ["tr", "tc", "p", "r"]:
176+
for y in ["tr", "tc", "p"]:
177177
# replace into xml code the row/paragraph/run containing
178178
# {%y xxx %} or {{y xxx}} template tag
179179
# by {% xxx %} or {{ xx }} without any surrounding <w:y> tags :
@@ -183,6 +183,18 @@ def cellbg(m):
183183
% {"y": y}
184184
)
185185
src_xml = re.sub(pat, r"\1 \2", src_xml, flags=re.DOTALL)
186+
187+
for y in ["p", "r"]:
188+
# replace into xml paragraph or run containing
189+
# {%rp xxx %} or {{rp xxx}} template tag
190+
# by {% xxx %} or {{ xx }} without any surrounding <w:p> tags
191+
# This allow for inline {rr <var> }} and paragraph {rp <var> }) styling
192+
# This is mandatory to have jinja2 generating correct xml code
193+
pat = (
194+
r"<w:%(y)s[ >](?:(?!<w:%(y)s[ >]).)*({%%|{{)r%(y)s ([^}%%]*(?:%%}|}})).*?</w:%(y)s>"
195+
% {"y": y}
196+
)
197+
src_xml = re.sub(pat, r"\1 \2", src_xml, flags=re.DOTALL)
186198

187199
for y in ["tr", "tc", "p"]:
188200
# same thing, but for {#y xxx #} (but not where y == 'r', since that

tests/richtextparagraph.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Created : 2025-02-28
3+
4+
@author: Hannah Imrie
5+
"""
6+
7+
from docxtpl import DocxTemplate, RichText, RichTextParagraph
8+
9+
tpl = DocxTemplate("templates/richtext_paragraph_tpl.docx")
10+
11+
rtp = RichTextParagraph()
12+
rt = RichText()
13+
14+
rtp.add("The rich text paragraph function allows paragraph styles to be added to text",parastyle="myrichparastyle")
15+
16+
rtp.add("This allows for the use of")
17+
rtp.add("bullet\apoints.", parastyle="SquareBullet")
18+
19+
rt.add("This works with ")
20+
rt.add("Rich ", bold=True)
21+
rt.add("Text ", italic=True)
22+
rt.add("Strings", underline="single")
23+
rt.add(" too.")
24+
25+
rtp.add(rt, parastyle="SquareBullet")
26+
27+
context = {
28+
"example": rtp,
29+
}
30+
31+
tpl.render(context)
32+
tpl.save("output/richtext_paragraph.docx")
17.9 KB
Binary file not shown.

tests/templates/richtext_tpl.docx

2.62 KB
Binary file not shown.

0 commit comments

Comments
 (0)