diff --git a/docxtpl/__init__.py b/docxtpl/__init__.py
index 1c0e1df..0ec37f1 100644
--- a/docxtpl/__init__.py
+++ b/docxtpl/__init__.py
@@ -9,6 +9,6 @@ __version__ = "0.19.1"
# flake8: noqa
from .inline_image import InlineImage
from .listing import Listing
-from .richtext import RichText, R
+from .richtext import RichText, R, RichTextParagraph, RP
from .subdoc import Subdoc
from .template import DocxTemplate
diff --git a/docxtpl/richtext.py b/docxtpl/richtext.py
index 4cf9962..5959d68 100644
--- a/docxtpl/richtext.py
+++ b/docxtpl/richtext.py
@@ -128,5 +128,50 @@ class RichText(object):
def __html__(self):
return self.xml
+class RichTextParagraph(object):
+ """class to generate Rich Text Paragraphs when using templates variables
+
+ This is much faster than using Subdoc class,
+ but this only for texts OUTSIDE an existing paragraph.
+ """
+
+ def __init__(self, text=None, **text_prop):
+ self.xml = ""
+ if text:
+ self.add(text, **text_prop)
+
+ def add(
+ self,
+ text,
+ parastyle=None,
+ ):
+
+ # If a RichText is added
+ if not isinstance(text, RichText):
+ text = RichText(text)
+
+ prop = ""
+ if parastyle:
+ prop += '' % parastyle
+
+ xml = ""
+ if prop:
+ xml += "%s" % prop
+ xml += text.xml
+ xml += ""
+ self.xml += xml
+
+ def __unicode__(self):
+ return self.xml
+
+ def __str__(self):
+ return self.xml
+
+ def __html__(self):
+ return self.xml
+
+
+
R = RichText
+RP = RichTextParagraph
diff --git a/docxtpl/template.py b/docxtpl/template.py
index b4e4378..e873958 100644
--- a/docxtpl/template.py
+++ b/docxtpl/template.py
@@ -161,7 +161,7 @@ class DocxTemplate(object):
flags=re.DOTALL,
)
src_xml = re.sub(
- r"({{r\s.*?}}|{%r\s.*?%})",
+ r"({{[rq]\s.*?}}|{%[rq].\s.*?%})",
r'\1',
src_xml,
flags=re.DOTALL,
@@ -184,6 +184,18 @@ class DocxTemplate(object):
% {"y": y}
)
src_xml = re.sub(pat, r"\1 \2", src_xml, flags=re.DOTALL)
+
+ # For paragraph level richtext
+ # replace into xml paragraph containing
+ # {%q xxx %} or {{q xxx}} template tag
+ # by {% xxx %} or {{ xx }} without any surrounding tags
+ # This allow for inline {r }} and paragraph {q }) styling
+ # This is mandatory to have jinja2 generating correct xml code
+ pat = (
+ r"](?:(?!]).)*({%%|{{)q ([^}%%]*(?:%%}|}})).*?"
+
+ )
+ src_xml = re.sub(pat, r"\1 \2", src_xml, flags=re.DOTALL)
for y in ["tr", "tc", "p"]:
# same thing, but for {#y xxx #} (but not where y == 'r', since that
diff --git a/tests/richtextparagraph.py b/tests/richtextparagraph.py
new file mode 100644
index 0000000..e63f91c
--- /dev/null
+++ b/tests/richtextparagraph.py
@@ -0,0 +1,43 @@
+"""
+Created : 2025-02-28
+
+@author: Hannah Imrie
+"""
+
+from docxtpl import DocxTemplate, RichText, RichTextParagraph
+
+tpl = DocxTemplate("templates/richtext_paragraph_tpl.docx")
+
+rtp = RichTextParagraph()
+rt = RichText()
+
+rtp.add("The rich text paragraph function allows paragraph styles to be added to text",parastyle="myrichparastyle")
+rtp.add("Any built in paragraph style can be used", parastyle="IntenseQuote")
+rtp.add("or you can add your own, unlocking all style options", parastyle="createdStyle")
+rtp.add("To use, just create a style in your template word doc with the formatting you want and call it in the code.", parastyle="normal")
+
+rtp.add("This allows for the use of")
+rtp.add("custom bullet\apoints", parastyle="SquareBullet")
+rtp.add("Numbered Bullet Points", parastyle="BasicNumbered")
+rtp.add("and Alpha Bullet Points.", parastyle="alphaBracketNumbering")
+rtp.add("You can", parastyle="normal")
+rtp.add("set the", parastyle="centerAlign")
+rtp.add("text alignment", parastyle="rightAlign")
+rtp.add("as well as the spacing between lines of text. Like this for example, this text has very tight spacing between the lines.\aIt also has no space between paragraphs of the same style.", parastyle="TightLineSpacing")
+rtp.add("Unlike this one, which has extra large spacing between lines for when you want to space things out a bit or just write a little less.", parastyle="WideLineSpacing")
+rtp.add("You can also set the background colour of a line.", parastyle="LineShadingGreen")
+
+rt.add("This works with ")
+rt.add("Rich ", bold=True)
+rt.add("Text ", italic=True)
+rt.add("Strings", underline="single")
+rt.add(" too.")
+
+rtp.add(rt, parastyle="SquareBullet")
+
+context = {
+ "example": rtp,
+}
+
+tpl.render(context)
+tpl.save("output/richtext_paragraph.docx")
\ No newline at end of file
diff --git a/tests/templates/richtext_paragraph_tpl.docx b/tests/templates/richtext_paragraph_tpl.docx
new file mode 100644
index 0000000..993b466
Binary files /dev/null and b/tests/templates/richtext_paragraph_tpl.docx differ
diff --git a/tests/templates/richtext_tpl.docx b/tests/templates/richtext_tpl.docx
index bcf589e..be34203 100644
Binary files a/tests/templates/richtext_tpl.docx and b/tests/templates/richtext_tpl.docx differ