Why does python-docx ignore rlt = true? - python

I need to write into a docx file from python (which i'm a bit new at) but i have to do it written rtl. After days of googling, the best I could do is this:
from docx import Document
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT as WD_STYLE_TYPE
from docx.shared import Pt
from docx.shared import Inches, Pt
# create docx file
document = Document()
# create paragraph
para = document.add_paragraph()
# create run
run = para.add_run("Hello World")
# create style
mystyle = document.styles.add_style("mystyle", 2)
run.style = mystyle
font = run.font
font.rtl = True # SET RIGHT TO LEFT
document.save(r"C:\Users\USER\Desktop\Code\TofesEfes\WordTes.docx")
the problem is that for some reason the code just ignores this line:
font.rtl = True # SET RIGHT TO LEFT
If I try to change it to:
font.bold = True # SET FONT TO BOLD
the font will come out bold.
I also tried changing the text to be in a rtl languge and nothin changed.
Does anyone here have any idea why it's doing this?

Related

Python how to delete first line of word file and font.bold not working

I am writing a python program that open a docx file and writing text into it. using "aspose.words"
and I have two problems:
I have a problem that when I open a file its starting with the sentence
"Evaluation Only. Created with Aspose.Words. Copyright 2003-2021 Aspose Pty Ltd."
and I want to delete that line after I creating the file (I can delete it manually so it's deletable)
my second problem is when I am using "font.bold = True" on an english text it's working just fine but when I am using it on an text that in other language its doesen't work.
Someone know how can I solves those two problems (it's the first time I am using "aspose.words")
here is my code:
import aspose.words as aw
def main():
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
writeDest(1, builder)
doc.save("out.docx")
def writeDest(designation, builder):
font = builder.font
font.size = 12
font.bold = True
font.name = "David"
paragraphFormat = builder.paragraph_format
paragraphFormat.alignment = aw.ParagraphAlignment.RIGHT
label = 'ייעוד: ' + str(designation)
builder.write(label)
builder.write("\n")
font.bold = False
if designation == 1:
file = open('destenationTextFiles/1', encoding="utf8")
for word in file:
builder.write(word)
builder.write('\n')
font.bold = True
builder.write(':תיקון ')
builder.write("\n")
font.bold = False
file.close()
file = open("destenationTextFiles/fixed1", encoding="utf8")
for word in file:
builder.write(word)
file.close()
if __name__ == "__main__":
main()
This message indicates you are using Aspose.Words in evaluation mode. Please see the following article to learn more about evaluation version limitations of Aspose.Words.
To test Aspose.Words for Python without these limitations you can request a temporary 30 days license.
To format right-to-left text you should use bidi font properties. For example see the following python code:
import aspose.words as aw
def main():
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Define a set of font settings for left-to-right text.
builder.font.name = "Courier New"
builder.font.size = 16
builder.font.italic = False
builder.font.bold = False
builder.font.locale_id = 1033
# Define another set of font settings for right-to-left text.
builder.font.name_bi = "David"
builder.font.size_bi = 24
builder.font.italic_bi = True
builder.font.bold_bi = True
builder.font.locale_id_bi = 1037;
# We can use the Bidi flag to indicate whether the text we are about to add
# with the document builder is right-to-left. When we add text with this flag set to true,
# it will be formatted using the right-to-left set of font settings.
builder.font.bidi = True
builder.write("ברוך הבא")
# Set the flag to false, and then add left-to-right text.
# The document builder will format these using the left-to-right set of font settings.
builder.font.bidi = False
builder.write(" Hello world!")
doc.save("C:\\Temp\\Font.Bidi.docx")
if __name__ == "__main__":
main()

cant change font on openpyxl

i am trying to make an automated xlsx document creator, for this i want to make some of the cells to have bold font, this is part of the code i use
from openpyxl import load_workbook
from openpyxl.styles import Font
from openpyxl.styles.colors import Color
wb_orden = load_workbook(filename = 'resg.xlsx')
ws_orden = wb_orden["Resguardo"]
italic24Font = Font( size = 24, italic = True, bold = True)
ws_orden['A6'] = "hsadgfagahgafdhg"
ws_orden['A6'].font = italic24Font
NAME = "generated/"+"res_"+order+".xlsx"
wb_orden.save(filename = NAME)
but when i see the xlsx file the A6 cell is changed to hsadgfagahgafdhg, but it isn't bold. thanks for reading i hope you can help me whit this
i also tried this but still not working
ws_orden['A6'].font = Font(size=23, underline='single', color='FFBB00', bold=True, italic=True)

Write a word document with each character having a different font

I have 3 fonts. I want to modify the Word document such that each character (alphabet) is assigned one of the three fonts randomly or in some order, I don't mind. No two consecutive characters should be of the same font.
I tried writing a python script but the much I tried to understand the docx-Python library I think only the paragraph-level styling is possible.
This is what I attempted:
import docx
from docx.shared import Pt
doc = docx.Document("Hey.docx")
mydoc = docx.Document()
style1 = mydoc.styles['Normal']
font1=style1.font
font1.name = 'Times New Roman'
font1.size = Pt(12.5)
style2 = mydoc.styles['Normal']
font2=style2.font
font2.name = 'Arial'
font2.size = Pt(15)
all_paras = doc.paragraphs
for para in all_paras:
mydoc.add_paragraph(para.text,style=style1)
print("-------")
mydoc.save("bye.docx")
If hey.docx has "Hello" as text : Bye.docx should have "H(in font A)e(in font B) l(in font C)l(in font A)o(in font B)"
Add each character as a separate run within the paragraph and assign a character style to each run.
from docx import Document
from docx.enum.style import WD_STYLE_TYPE as ST
document = Document()
styles = document.styles
style_A = styles.add_style("A", ST.CHARACTER)
style_A.font.name = "Arial"
style_A.font.size = Pt(15)
style_B = styles.add_style("B", ST.CHARACTER)
style_B.font.name = "Times New Roman"
style_B.font.size = Pt(12.5)
paragraph = document.add_paragraph()
for idx, char in enumerate("abcde"):
paragraph.add_run(char, style_A if idx % 2 else style_B)
document.save("output.docx")
I'll leave it to you to create additional character styles and invent a more sophisticated way of determining which style to assign to each character.

How to update table of contents in docx-file with python on linux?

I've got a problem with updating table of contents in docx-file, generated by python-docx on Linux. Generally, it is not difficult to create TOC (Thanks for this answer https://stackoverflow.com/a/48622274/9472173 and this thread https://github.com/python-openxml/python-docx/issues/36)
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
paragraph = self.document.add_paragraph()
run = paragraph.add_run()
fldChar = OxmlElement('w:fldChar') # creates a new element
fldChar.set(qn('w:fldCharType'), 'begin') # sets attribute on element
instrText = OxmlElement('w:instrText')
instrText.set(qn('xml:space'), 'preserve') # sets attribute on element
instrText.text = 'TOC \o "1-3" \h \z \u' # change 1-3 depending on heading levels you need
fldChar2 = OxmlElement('w:fldChar')
fldChar2.set(qn('w:fldCharType'), 'separate')
fldChar3 = OxmlElement('w:t')
fldChar3.text = "Right-click to update field."
fldChar2.append(fldChar3)
fldChar4 = OxmlElement('w:fldChar')
fldChar4.set(qn('w:fldCharType'), 'end')
r_element = run._r
r_element.append(fldChar)
r_element.append(instrText)
r_element.append(fldChar2)
r_element.append(fldChar4)
p_element = paragraph._p
But later to make TOC visible it requires to update fields. Mentioned bellow solution involves update it manually (right-click on TOC hint and choose 'update fields'). For the automatic updating, I've found the following solution with word application simulation (thanks to this answer https://stackoverflow.com/a/34818909/9472173)
import win32com.client
import inspect, os
def update_toc(docx_file):
word = win32com.client.DispatchEx("Word.Application")
doc = word.Documents.Open(docx_file)
doc.TablesOfContents(1).Update()
doc.Close(SaveChanges=True)
word.Quit()
def main():
script_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
file_name = 'doc_with_toc.docx'
file_path = os.path.join(script_dir, file_name)
update_toc(file_path)
if __name__ == "__main__":
main()
It pretty works on Windows, but obviously not on Linux. Have someone any ideas about how to provide the same functionality on Linux. The only one suggestion I have is to use local URLs (anchors) to every heading, but I am not sure is it possible with python-docx, also I'm not very strong with these openxml features. I will very appreciate any help.
I found a solution from this Github Issue. It work on ubuntu.
def set_updatefields_true(docx_path):
namespace = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
doc = Document(docx_path)
# add child to doc.settings element
element_updatefields = lxml.etree.SubElement(
doc.settings.element, f"{namespace}updateFields"
)
element_updatefields.set(f"{namespace}val", "true")
doc.save(docx_path)## Heading ##
import docx.oxml.ns as ns
def update_table_of_contents(doc):
# Find the settings element in the document
settings_element = doc.settings.element
# Create an "updateFields" element and set its "val" attribute to "true"
update_fields_element = docx.oxml.shared.OxmlElement('w:updateFields')
update_fields_element.set(ns.qn('w:val'), 'true')
# Add the "updateFields" element to the settings element
settings_element.append(update_fields_element)

python docx set table cell background and text color

I am using python 2.7 with docx and I would like to change the background and text color of cells in my table based on condition.
I could not find any usefull resources about single cell formatting
Any suggestions?
Edit 1
my code
style_footer = "DarkList"
style_red = "ColorfulList"
style_yellow = "LightShading"
style_green = "MediumShading2-Accent6"
style_transperent = "TableNormal"
for a,rec in enumerate(data):
#V headinh se piše prvo polje iz table heada
document.add_heading(rec['tableHead'][0][0], level=1)
image_path = imageFolder + "\\" + slike[a]
document.add_picture(image_path, height=Inches(3.5))
#y += 28
#worksheet.insert_image( y, 1,imageFolder + "/" + slike[a])
for i, head in enumerate(rec['tableHead']):
table = document.add_table(rows=1, cols = len(head))
hdr_cells = table.rows[0].cells
for a in range(0,len(head)):
hdr_cells[a].text = head[a]
for a,body in enumerate(rec['tableData']):
row_cells = table.add_row().cells
for a in range(0,len(body)):
if body[a]['style'] == 'footer':
stil = style_footer
elif body[a]['style'] == 'red':
stil = style_red
elif body[a]['style'] == 'yellow':
stil = style_yellow
elif body[a]['style'] == 'green':
stil = style_green
else:
stil = style_transperent
row_cells[a].add_paragraph(body[a]['value'], stil)
document.save(wordDoc)
All cells are still the same.
If you want to color fill a specific cell in a table you can use the code below.
For example let's say you need to fill the first cell in the first row of your table with the RGB color 1F5C8B:
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
shading_elm_1 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
table.rows[0].cells[0]._tc.get_or_add_tcPr().append(shading_elm_1)
Now if you want to also fill the second cell in the first row with the same color, you should create a new element
otherwise if you use the same element as above the fill will move on and will disappear from the first cell...
shading_elm_2 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
table.rows[0].cells[1]._tc.get_or_add_tcPr().append(shading_elm_2)
...and so on for other cells.
Source: https://groups.google.com/forum/#!topic/python-docx/-c3OrRHA3qo
With Nikos Tavoularis' solution, we have to create a new element for every cell.
I have created a function that achieves this. Works in Python revision 3.5.6 and python-docx revision 0.8.10
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
def set_table_header_bg_color(table.rows[row_ix].cell):
"""
set background shading for Header Rows
"""
tblCell = cell._tc
tblCellProperties = tblCell.get_or_add_tcPr()
clShading = OxmlElement('w:shd')
clShading.set(qn('w:fill'), "00519E") #Hex of Dark Blue Shade {R:0x00, G:0x51, B:0x9E}
tblCellProperties.append(clShading)
return cell
"""
End of set_table_header_bg_color Function
"""
# main function
"""
..
..
..
1. Load Document
..
2. Access the required section
..
3. Load the required Table
..
4. Traverse to the cell by accessing the rows object
..
"""
for each_row in table.rows :
for each_cell in each_row.cells:
if each_cell.value satisfies a condition:
set_table_header_bg_color(each_cell)
"""
5. Continue execution
"""
What we found is that, if you do cell.add_paragraph('sometext', style_object), it will keep the existing empty paragraph and add an additional paragraph with the style, which is not ideal.
What you will want to do is something like:
# replace the entire content of cell with new text paragraph
cell.text = 'some text'
# assign new style to the first paragraph
cell.paragraphs[0].style = style_object
Note that the style is applied to the paragraph not the cell, which isn't ideal for background colors (since it won't fill the enter cell if you have some padding. I haven't found a way around that (except in the case where you want EVERY cell to have a background color, you can apply a style to table.style).
Also, make sure that your styles are defined. You can check
styles = documents.styles
for s in styles:
print s.name
to see all the styles you have. You can define new styles and also load a template document with pre-defined styles already.
It looks like instead of using the cell.text = "Something" method you need to use the cell.add_paragraph("SomeText", a_style) with a defined style - probably one of:
ColorfulGrid
ColorfulGrid-Accent1
ColorfulGrid-Accent2
ColorfulGrid-Accent3
ColorfulGrid-Accent4
ColorfulGrid-Accent5
ColorfulGrid-Accent6
Full list here.
If you use the “default” template document - otherwise you will have to create your own.
Taking from Nikos Tavoularis answer I would just change the shading_elm_1 declaration, as if you include the cell color in a loop for instance things might get messy.
As such, my suggestion would be:
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
table.rows[0].cells[0]._tc.get_or_add_tcPr().append(parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w'))))
I made a video demonstrating a way to do it here I took inspiration from the people above but I still had issues so I made this too help others.
https://www.youtube.com/watch?v=1Mgb95yigkk&list=PL_W7lgC2xeJfWBUllp7ALKOM5GUBMCVoP
from docx import Document
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
document = Document("youfile.docx")
Table = document.tables[0]
#GET CELLS XML ELEMENT
cell_xml_element = Table.rows[1].cells[0]._tc
#RETRIEVE THE TABLE CELL PROPERTIES
table_cell_properties = cell_xml_element.get_or_add_tcPr()
#CREATE SHADING OBJECT
shade_obj = OxmlElement('w:shd')
#SET THE SHADING OBJECT
shade_obj.set(qn('w:fill'), "ff00ff")
#APPEND THE PROPERTIES TO THE TABLE CELL PROPERTIES
table_cell_properties.append(shade_obj)
document.save("yoursavefile.docx")
The code above will change the first cellof the second row of the first table in the document.Example of the output.
If you want to loop through the cells in a row use:
def color_row(row=0):
'make row of cells background colored, defaults to column header row'
row = t.rows[row]
for cell in row.cells:
shading_elm_2 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
cell._tc.get_or_add_tcPr().append(shading_elm_2)
run the function to color cells in the second row
color_row(2)
If you want to change the text color too, you can set it on the runs within the cell. I wrote this function to handle the cell background and text colors together (using Nikos' method for the fill):
def shade_cell(cell, fill=None, color=None):
if fill:
shading_elm = parse_xml(r'<w:shd {} w:fill="{}"/>'.format(nsdecls('w'), fill))
cell._tc.get_or_add_tcPr().append(shading_elm)
if color:
for p in cell.paragraphs:
for r in p.runs:
r.font.color.rgb = RGBColor.from_string(color)
I originally tried to expand Nikos' solution by adding w:color="XXXXXX" to the w:shd tag but that didn't work for me. However setting the font color on each run got the result I wanted.
I have compiled the previous answers and added some features.
Feel free to test: Create new file run the "main" part at the bottom.
""" adder for python-docx in order to change text style in tables:
font color, italic, bold
cell background color
based on answers on
https://stackoverflow.com/questions/26752856/python-docx-set-table-cell-background-and-text-color
"""
import docx # import python-docx (in order to create .docx report file)
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
def change_table_cell(cell, background_color=None, font_color=None, font_size=None, bold=None, italic=None):
""" changes the background_color or font_color or font style (bold, italic) of this cell.
Leave the params as 'None' if you do not want to change them.
params:
cell: the cell to manipulate
background_color: name for the color, e.g. "red" or "ff0000"
font_color:
font_size: size in pt (e.g. 10)
bold: requested font style. True or False, or None if it shall remain unchanged
italic: requested font style. True or False, or None if it shall remain unchanged
background_color: the color of cells background"""
if background_color:
shading_elm = parse_xml(r'<w:shd {} w:fill="{}"/>'.format(nsdecls('w'), background_color))
cell._tc.get_or_add_tcPr().append(shading_elm)
if font_color:
for p in cell.paragraphs:
for r in p.runs:
r.font.color.rgb = docx.shared.RGBColor.from_string(font_color)
if font_size:
for p in cell.paragraphs:
for r in p.runs:
r.font.size = docx.shared.Pt(font_size)
if bold is not None:
for p in cell.paragraphs:
for r in p.runs:
r.bold = bold
if italic is not None:
for p in cell.paragraphs:
for r in p.runs:
r.italic = italic
def change_table_row(table_row, background_color=None, font_color=None, font_size=None, bold=None, italic=None):
for cell in table_row.cells:
change_table_cell(cell, background_color=background_color, font_color=font_color, font_size=font_size,
bold=bold,
italic=italic)
if __name__ == "__main__": # do the following code only if we run the file itself
#document = docx.Document('template.docx') # create an instance of a word document, use the style that we have defined in 'template.docx'
document = docx.Document()
num_rows = 4
num_cols = 3
table = document.add_table(rows=num_rows, cols=num_cols) # create empty table
#table.style = document.styles['MyTableStyleBlue'] # test overwriting the predefined style
# fill table
for row in range(num_rows):
for col in range(num_cols):
table.rows[row].cells[col].text = f'row/col=({row},{col})'
""" change color (see https://stackoverflow.com/questions/26752856/python-docx-set-table-cell-background-and-text-color) """
# Nikos Tavoularis answered Apr 18, 2017 at 8:38
shading_elm_1 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
table.rows[0].cells[0]._tc.get_or_add_tcPr().append(shading_elm_1)
# test new function derived from dyoung's answere of May 25 at 7:34, 2022
change_table_cell(table.rows[0].cells[0], background_color=None, font_color="ff0000", bold=False)
change_table_cell(table.rows[1].cells[2], background_color="00ff00", font_color="ff0000", font_size=20, bold=True)
change_table_row(table.rows[3], background_color="lightgreen", font_color="0000ff", italic=True) # https://www.delftstack.com/howto/python/colors-in-python/
document.save('table_shading_test.docx')

Categories