@Tushar8874
What you are seeing
When you replace a placeholder that lives inside a table cell the new text is written into the same rectangle that originally held the placeholder.
If the replacement string is longer than that rectangle Aspose‑PDF does not automatically increase the height of the cell – the extra part is simply cut off.
To make the text wrap inside the cell you have to tell Aspose‑PDF that the cell (or the fragment that you are writing) is word‑wrap‑enabled.
How to enable wrapping in table cells
1. Turn on word‑wrapping for every cell that can contain a placeholder
import aspose.pdf as ap
import io
import re
def enable_word_wrap(doc):
"""
Walk through all pages → tables → rows → cells and set
cell.IsWordWrapped = True so that any text written later
will automatically wrap to the next line and increase the
cell height if required.
"""
for page in doc.pages:
# The page may contain a TableAbsorber; we can also just
# enumerate the page's Paragraphs collection and look for
# Table objects.
for paragraph in page.paragraphs:
if isinstance(paragraph, ap.Table):
for row in paragraph.rows:
for cell in row.cells:
cell.is_word_wrapped = True # <-- key line
# (optional) let the cell grow vertically
cell.fit_content = True # prevents clipping
Why fit_content?
It tells the renderer to enlarge the cell height if the wrapped text needs more space. Without it the renderer will keep the original height and you will still see clipping for very long strings.
Add the call before you start the placeholder replacement:
def fill_pdf_in_memory(input_pdf_path, replacements):
doc = ap.Document(input_pdf_path)
# <‑‑‑ enable wrapping for all cells
enable_word_wrap(doc)
for placeholder, new_value in replacements.items():
patterns = build_patterns(placeholder)
replaced = False
for pattern in patterns:
absorber = ap.text.TextFragmentAbsorber(pattern)
absorber.text_search_options = ap.text.TextSearchOptions(True)
doc.pages.accept(absorber)
fragments = absorber.text_fragments
if fragments and len(fragments) > 0:
for frag in fragments:
# -----------------------------------------------------------------
# 1️⃣ Ensure the fragment itself is allowed to wrap
# -----------------------------------------------------------------
frag.is_wrapped = True # new in 23.xx+
# -----------------------------------------------------------------
# 2️⃣ Replace the text
# -----------------------------------------------------------------
# You can use UpdateText() to have Aspose‑PDF recalc the layout
# with wrapping taken into account.
update_opts = ap.UpdateTextOptions()
update_opts.is_word_wrapped = True # same effect as frag.is_wrapped
frag.update_text(new_value, update_opts)
replaced = True
break
if not replaced:
print(f"WARNING: Placeholder not found → {placeholder}")
mem_stream = io.BytesIO()
doc.save(mem_stream)
return mem_stream.getvalue()
2. What the code does
| Step |
Explanation |
cell.is_word_wrapped = True |
Marks the cell as wrappable. When the renderer meets a line that exceeds the cell width it breaks the line and pushes the extra part to a new line. |
cell.fit_content = True |
Allows the cell height to grow automatically to accommodate the wrapped lines. |
frag.is_wrapped = True (or UpdateTextOptions().is_word_wrapped = True) |
Guarantees that the fragment you are writing respects the wrap flag. This is useful when you replace a fragment that lives outside a Table as well. |
frag.update_text(..) |
Re‑lays out the fragment after the new text is set, so the internal layout engine can recompute line breaks and cell size. Using the simple frag.text = new_value works for short strings but does not trigger a re‑layout, which is why long strings get clipped. |
Relevant Aspose.PDF for Python‑via‑.NET documentation
Frequently asked follow‑up questions
| Q |
A |
| Do I need to set the same properties for every cell? |
If the entire table can contain placeholders, the safest approach is to enable wrapping for all cells as shown above. If you know the exact columns/rows you can limit the loop to those cells. |
| My cells have a fixed height in the original template – will they still grow? |
Setting cell.fit_content = True overrides the fixed height only when the content exceeds the current height. If you want to preserve the original height even when the text is long, you would have to manually increase the height (cell.height = …) before replacement. |
| What about vertical alignment (top/middle/bottom) after wrapping? |
After wrapping the cell height is adjusted; the existing cell.vertical_alignment property (default is Top) will still be honoured. You can change it to Middle or Bottom if the design requires it. |
| Can I keep the original font/size from the placeholder? |
Yes. The fragment you obtain from the absorber already carries the original TextState. When you call frag.update_text(new_value, opts) the original TextState is retained, so the font, size, color, etc., remain unchanged. |
| I’m using a PDF/A or a tagged PDF – does this affect wrapping? |
No. The wrap logic works on the visual layout layer and is independent of PDF/A compliance or tagging. Tag information is preserved when you save the document. |
TL;DR – What you need to add to your existing code
# 1️⃣ Enable wrapping for every cell once (before any replacement)
enable_word_wrap(doc)
# 2️⃣ When you replace a fragment, use UpdateText with wrapping enabled
for frag in fragments:
frag.is_wrapped = True # optional but good practice
opts = ap.UpdateTextOptions()
opts.is_word_wrapped = True
frag.update_text(new_value, opts)
That’s all – after these two small changes the replacement text will wrap inside the cell, the cell height will grow as needed, and the overall table layout will stay intact.
If you run into any edge‑cases (e.g., tables with merged cells, rotated cells, or absolute‑positioned tables), feel free to drop a follow‑up message with a short sample PDF and we’ll fine‑tune the solution. Happy coding! 