How to bold the text of a row cells in python docx? - python

I am generating a doc using python docx module.
I want to bold the specific cell of a row in python docx
here is the code
book_title = '\n๐“๐ข๐ญ๐ฅ๐ž-:\n {}\n\n'.format(book_title)
book_desc = '๐€๐ฎ๐ญ๐ก๐จ๐ซ-: {}\n\n๐ƒ๐ž๐ฌ๐œ๐ซ๐ข๐ฉ๐ญ๐ข๐จ๐ง:\n{}\n\n๐’๐š๐ฅ๐ž๐ฌ ๐๐จ๐ข๐ง๐ญ๐ฌ:\n{}'.format(book.author,book_description,sales_point)
row1.cells[1].text = (book_title + book_desc)
I just want to bold the book_title.
If I apply a style it automatically applies to whole document.

A cell does not have a character style; character style can only be applied to text, and in particular to a run of text. This is in fact the defining characteristic of a run, being a sequence of characters that share the same character formatting, also known as font in python-docx.
To get the book title with a different font than the description, they need to appear in separate runs. Assigning to Cell.text (as you have) results in all the text being in a single run.
This might work for you, but assumes the cell is empty as you start:
paragraph = row1.cells[1].paragraphs[0]
title_run = paragraph.add_run(book_title)
description_run = paragraph.add_run(book_desc)
title_run.bold = True
This code can be made more compact:
paragraph = row1.cells[1].paragraphs[0]
paragraph.add_run(book_title).bold = True
paragraph.add_run(book_desc)
but perhaps the former version makes it more clear just what you're doing in each step.

Here is how I understand it:
Paragraph is holding the run objects and styles (bold, italic) are methods of run.
So following this logic here is what might solve your question:
row1_cells[0].paragraphs[0].add_run(book_title + book_desc).bold=True
This is just an example for the first cell of the table. Please amend it in your code.

Since you are using the docx module, you can style your text/paragraph by explicitly defining the style.
In order to apply a style, use the following code snippet referenced from docx documentation here.
>>> from docx import Document
>>> document = Document()
>>> style = document.styles['Normal']
>>> font = style.font
>>> font.bold= True
This will change the font style to bold for the applied paragraph.

In python-docx, the styling of any character in a docx template document can be overridden by the use of Rich Text styling. You should provide a context variable for the particular character/string that needs styling in your template, at the position of the character/string. This variable maps to the RichText object that has the style definition(that you define in your code), to style the character/string. To make things clearer, consider an example template doc "test.docx" that contains the following text:
Hello {{r context_var}}!
The {{..}} is the jinja2 tag syntax and {{r is the RichText tag that overrides the character styling. The context_var is a variable that maps the styling to your character string.
We accomplish Rich Text styling like this:
from docxtpl import DocxTemplate, RichText
doc = DocxTemplate("test.docx")
rt = RichText() #create a RichText object
rt.add('World', bold=True) #pass the text as an argument and the style, bold=True
context = { 'context_var': rt } #add context variable to the context and map it to rt
doc.render(context) #render the context
doc.save("generated_doc.docx") #save as a new document
Let's look at the contents of "generated_doc.docx":
Hello World!
I'm not sure how your template is designed, but if you just want the book_title as bold, your template "test.docx" should have text like:
Title:-
{{r book_title_var}}
The code should be modified to:
book_title = "Lord of the Rings" #or wherever you get the book title from
rt.add(book_title, bold=True)
context = { 'book_title_var': rt }
generated_doc.docx:
Title:-
Lord of the Rings

Related

How to get actual style of text in word document using python docx

I am using python docx library to read MS word file(.docx). When i read paragraph i use font function to get all style properties. But sometimes it gives None for font size attribute. Is there any way to get actual font size which paragraph contains.
Example code is given below which i am using to parse paragraphs
from docx import Document
d = Document(document_path)
for paragraph in d.paragraphs:
for run in paragraph.runs:
print (run.font.size)
Short answer is no. What you're asking for is effective font size and python-docx can only see an explicitly set font size. When font.size reports None, it is the default for that paragraph, whatever that is, which depends on the style hierarchy.
In many cases it might be the font size of the applicable paragraph style, but the only way to know for sure is to traverse the style hierarchy for that text node to the first explicit definition.
The following code worked for me:
Divide it by 12700 to get actually font size.
import docx
docFile = docx.Document("C:/Users/vjadhav6/Desktop/testFile.docx")
for i in docFile.paragraphs:
for j in i.runs:
print(j.font.size/12700)

How to wrap cell text in tables via docx library or xml?

I have been using python docx library and oxml to automate some changes to my tables in my word document. Unfortunately, no matter what I do, I cannot wrap the text in the table cells.
I managed to successfully manipulate 'autofit' and 'fit-text' properties of my table, but non of them contribute to the wrapping of the text in the cells. I can see that there is a "w:noWrap" in the xml version of my word document and no matter what I do I cannot manipulate and remove it. I believe it is responsible for the word wrapping in my table.
for example in this case I am adding a table. I can fit text in cell and set autofit to 'true' but cannot for life of me wrap the text:
from docx import Document
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
doc = Document()
table = doc.add_table(5,5)
table.autofit = True # Does Autofit but not wrapping
tc = table.cell(0,0)._tc # As a test, fit text to cell 0,0
tcPr = tc.get_or_add_tcPr()
tcFitText = OxmlElement('w:tcFitText')
tcFitText.set(qn('w:val'),"true")
tcPr.append(tcFitText) #Does fitting but no wrapping
doc.save('demo.docx')
I would appreciate any help or hints.
The <w:noWrap> element appears to be a child of <w:tcPr>, the element that controls table cell properties.
You should be able to access it from the table cell element using XPath:
tc = table.cell(0, 0)._tc
noWraps = tc.xpath(".//w:noWrap")
The noWraps variable here will then be a list containing zero or more <w:noWrap> elements, in your case probably one.
Deleting it is probably the simplest approach, which you can accomplish like this:
if noWraps: # ---skip following code if list is empty---
noWrap = noWraps[0]
noWrap.getparent().remove(noWrap)
You can also take the approach of setting the value of the w:val attribute of the w:noWrap element, but then you have to get into specifying the Clark name of the attribute namespace, which adds some extra fuss and doesn't really produce a different outcome unless for some reason you want to keep that element around.

Python-docx style format

I am using python-docx to output a Pandas DataFrame to a Word table. About a year ago, I wrote this code to build that table, which worked at the time:
table = Rpt.add_table(rows=1, cols=(df.shape[1]+1))
table.style = 'LightShading-Accent2'
Where Rpt is a Document from a template. Now, I get an error:
KeyError: "no style with name 'LightShading-Accent2'"
How should I go about defining the style? Has the naming convention changed in newer versions of python-docx?
Yes, slightly, apologies for that, but it was the right long-term decision for the API.
Try using "Light Shading - Accent 2" instead. It should be the name just as it appears in the Word user interface (UI).
If you still can't get it, you can enumerate all the style names with something like this:
from docx import Document
document = Document()
styles = document.styles
for style in styles:
print "'%s' -- %s" % (style.name, style.type)
If you want to narrow it down a bit, say to only table styles, you can add this:
from docx.enum.style import WD_STYLE_TYPE
styles = [s for s in document.styles if s.type == WD_STYLE_TYPE.TABLE]
for style in styles:
print(style.name)
The printed names will give you the exact spellings.

Add an image in a specific position in the document (.docx)?

I use Python-docx to generate Microsoft Word document.The user want that when he write for eg: "Good Morning every body,This is my %(profile_img)s do you like it?"
in a HTML field, i create a word document and i recuper the picture of the user from the database and i replace the key word %(profile_img)s by the picture of the user NOT at the END OF THE DOCUMENT. With Python-docx we use this instruction to add a picture:
document.add_picture('profile_img.png', width=Inches(1.25))
The picture is added to the document but the problem that it is added at the end of the document.
Is it impossible to add a picture in a specific position in a microsoft word document with python? I've not found any answers to this in the net but have seen people asking the same elsewhere with no solution.
Thanks (note: I'm not a hugely experiance programmer and other than this awkward part the rest of my code will very basic)
Quoting the python-docx documentation:
The Document.add_picture() method adds a specified picture to the end of the document in a paragraph of its own. However, by digging a little deeper into the API you can place text on either side of the picture in its paragraph, or both.
When we "dig a little deeper", we discover the Run.add_picture() API.
Here is an example of its use:
from docx import Document
from docx.shared import Inches
document = Document()
p = document.add_paragraph()
r = p.add_run()
r.add_text('Good Morning every body,This is my ')
r.add_picture('/tmp/foo.jpg')
r.add_text(' do you like it?')
document.save('demo.docx')
well, I don't know if this will apply to you but here is what I've done to set an image in a specific spot to a docx document:
I created a base docx document (template document). In this file, I've inserted some tables without borders, to be used as placeholders for images. When creating the document, first I open the template, and update the file creating the images inside the tables. So the code itself is not much different from your original code, the only difference is that I'm creating the paragraph and image inside a specific table.
from docx import Document
from docx.shared import Inches
doc = Document('addImage.docx')
tables = doc.tables
p = tables[0].rows[0].cells[0].add_paragraph()
r = p.add_run()
r.add_picture('resized.png',width=Inches(4.0), height=Inches(.7))
p = tables[1].rows[0].cells[0].add_paragraph()
r = p.add_run()
r.add_picture('teste.png',width=Inches(4.0), height=Inches(.7))
doc.save('addImage.docx')
Here's my solution. It has the advantage on the first proposition that it surrounds the picture with a title (with style Header 1) and a section for additional comments. Note that you have to do the insertions in the reverse order they appear in the Word document.
This snippet is particularly useful if you want to programmatically insert pictures in an existing document.
from docx import Document
from docx.shared import Inches
# ------- initial code -------
document = Document()
p = document.add_paragraph()
r = p.add_run()
r.add_text('Good Morning every body,This is my ')
picPath = 'D:/Development/Python/aa.png'
r.add_picture(picPath)
r.add_text(' do you like it?')
document.save('demo.docx')
# ------- improved code -------
document = Document()
p = document.add_paragraph('Picture bullet section', 'List Bullet')
p = p.insert_paragraph_before('')
r = p.add_run()
r.add_picture(picPath)
p = p.insert_paragraph_before('My picture title', 'Heading 1')
document.save('demo_better.docx')
This is adopting the answer written by Robแตฉ while considering more flexible input from user.
My assumption is that the HTML field mentioned by Kais Dkhili (orignal enquirer) is already loaded in docx.Document(). So...
Identify where is the related HTML text in the document.
import re
## regex module
img_tag = re.compile(r'%\(profile_img\)s') # declare pattern
for _p in enumerate(document.paragraphs):
if bool(img_tag.match(_p.text)):
img_paragraph = _p
# if and only if; suggesting img_paragraph a list and
# use append method instead for full document search
break # lose the break if want full document search
Replace desired image into placeholder identified as img_tag = '%(profile_img)s'
The following code is after considering the text contains only a single run
May be changed accordingly if condition otherwise
temp_text = img_tag.split(img_paragraph.text)
img_paragraph.runs[0].text = temp_text[0]
_r = img_paragraph.add_run()
_r.add_picture('profile_img.png', width = Inches(1.25))
img_paragraph.add_run(temp_text[1])
and done. document.save() it if finalised.
In case you are wondering what to expect from the temp_text...
[In]
img_tag.split(img_paragraph.text)
[Out]
['This is my ', ' do you like it?']
I spend few hours in it. If you need to add images to a template doc file using python, the best solution is to use python-docx-template library.
Documentation is available here
Examples available in here
This is variation on a theme. Letting I be the paragraph number in the specific document then:
p = doc.paragraphs[I].insert_paragraph_before('\n')
p.add_run().add_picture('Fig01.png', width=Cm(15))

Get font type in python odfpy?

Have somebody experencies with odfpy ?
I parsed document with this python package and I got paragraphs with his text and stylenames, now I need to detect which type of text font is in this paragraphs text?
Do you have any ideas ?
The styles are defined separately from the text. The nodes that contain text will be inside nodes that have a style as an attribute. An example might look like this:
<text:p text:style-name="P5">
<text:span text:style-name="T1">Do donkeys eat macadamia nuts? And if they don't, why don't they?
</text:span>
</text:p>
In this example, two styles (P5 or T1) might specify a font for the text. You will need to look at the document's style definition section.
This code will create a dictionary containing the document's styles.
def get_styles(doc):
styles= {}
for ast in doc.automaticstyles.childNodes:
name= ast.getAttribute('name')
style= {}
styles[name]= style
for k in ast.attributes.keys():
style[k[1]]= ast.attributes[k]
for n in ast.childNodes:
for k in n.attributes.keys():
style[n.qname[1] + "/" + k[1]]= n.attributes[k]
return styles
You can then examine the relevant styles that correspond to the text you care about. Inside each style will be a style:text-properties element, and that element will have a style:font-name attribute that specifies a font.

Categories