Rich Text Paragraph Formatting Update
This commit is contained in:
parent
b97fa32f10
commit
aa7b7a77e3
@ -9,6 +9,6 @@ __version__ = "0.19.1"
|
|||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
from .inline_image import InlineImage
|
from .inline_image import InlineImage
|
||||||
from .listing import Listing
|
from .listing import Listing
|
||||||
from .richtext import RichText, R
|
from .richtext import RichText, R, RichTextParagraph, RP
|
||||||
from .subdoc import Subdoc
|
from .subdoc import Subdoc
|
||||||
from .template import DocxTemplate
|
from .template import DocxTemplate
|
||||||
|
|||||||
@ -119,5 +119,50 @@ class RichText(object):
|
|||||||
def __html__(self):
|
def __html__(self):
|
||||||
return self.xml
|
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 += '<w:pStyle w:val="%s"/>' % parastyle
|
||||||
|
|
||||||
|
xml = "<w:p>"
|
||||||
|
if prop:
|
||||||
|
xml += "<w:pPr>%s</w:pPr>" % prop
|
||||||
|
xml += text.xml
|
||||||
|
xml += "</w:p>"
|
||||||
|
self.xml += xml
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.xml
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.xml
|
||||||
|
|
||||||
|
def __html__(self):
|
||||||
|
return self.xml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R = RichText
|
R = RichText
|
||||||
|
RP = RichTextParagraph
|
||||||
|
|||||||
@ -160,7 +160,7 @@ class DocxTemplate(object):
|
|||||||
flags=re.DOTALL,
|
flags=re.DOTALL,
|
||||||
)
|
)
|
||||||
src_xml = re.sub(
|
src_xml = re.sub(
|
||||||
r"({{r\s.*?}}|{%r\s.*?%})",
|
r"({{r.\s.*?}}|{%r.\s.*?%})",
|
||||||
r'</w:t></w:r><w:r><w:t xml:space="preserve">\1</w:t></w:r><w:r><w:t xml:space="preserve">',
|
r'</w:t></w:r><w:r><w:t xml:space="preserve">\1</w:t></w:r><w:r><w:t xml:space="preserve">',
|
||||||
src_xml,
|
src_xml,
|
||||||
flags=re.DOTALL,
|
flags=re.DOTALL,
|
||||||
@ -173,7 +173,7 @@ class DocxTemplate(object):
|
|||||||
r"-%}(?:(?!<w:t[ >]|{%|{{).)*?<w:t[^>]*?>", "%}", src_xml, flags=re.DOTALL
|
r"-%}(?:(?!<w:t[ >]|{%|{{).)*?<w:t[^>]*?>", "%}", src_xml, flags=re.DOTALL
|
||||||
)
|
)
|
||||||
|
|
||||||
for y in ["tr", "tc", "p", "r"]:
|
for y in ["tr", "tc", "p"]:
|
||||||
# replace into xml code the row/paragraph/run containing
|
# replace into xml code the row/paragraph/run containing
|
||||||
# {%y xxx %} or {{y xxx}} template tag
|
# {%y xxx %} or {{y xxx}} template tag
|
||||||
# by {% xxx %} or {{ xx }} without any surrounding <w:y> tags :
|
# by {% xxx %} or {{ xx }} without any surrounding <w:y> tags :
|
||||||
@ -183,6 +183,18 @@ class DocxTemplate(object):
|
|||||||
% {"y": y}
|
% {"y": y}
|
||||||
)
|
)
|
||||||
src_xml = re.sub(pat, r"\1 \2", src_xml, flags=re.DOTALL)
|
src_xml = re.sub(pat, r"\1 \2", src_xml, flags=re.DOTALL)
|
||||||
|
|
||||||
|
for y in ["p", "r"]:
|
||||||
|
# replace into xml paragraph or run containing
|
||||||
|
# {%rp xxx %} or {{rp xxx}} template tag
|
||||||
|
# by {% xxx %} or {{ xx }} without any surrounding <w:p> tags
|
||||||
|
# This allow for inline {rr <var> }} and paragraph {rp <var> }) styling
|
||||||
|
# This is mandatory to have jinja2 generating correct xml code
|
||||||
|
pat = (
|
||||||
|
r"<w:%(y)s[ >](?:(?!<w:%(y)s[ >]).)*({%%|{{)r%(y)s ([^}%%]*(?:%%}|}})).*?</w:%(y)s>"
|
||||||
|
% {"y": y}
|
||||||
|
)
|
||||||
|
src_xml = re.sub(pat, r"\1 \2", src_xml, flags=re.DOTALL)
|
||||||
|
|
||||||
for y in ["tr", "tc", "p"]:
|
for y in ["tr", "tc", "p"]:
|
||||||
# same thing, but for {#y xxx #} (but not where y == 'r', since that
|
# same thing, but for {#y xxx #} (but not where y == 'r', since that
|
||||||
|
|||||||
32
tests/richtextparagraph.py
Normal file
32
tests/richtextparagraph.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
"""
|
||||||
|
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("This allows for the use of")
|
||||||
|
rtp.add("bullet\apoints.", parastyle="SquareBullet")
|
||||||
|
|
||||||
|
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")
|
||||||
BIN
tests/templates/richtext_paragraph_tpl.docx
Normal file
BIN
tests/templates/richtext_paragraph_tpl.docx
Normal file
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user