Python-docx - How to change table font size? - python-2.7

table = document.add_table(rows=1, cols=1)
hdr_cells = table.rows[0].cells
hdr_cells[0].text = 'Qty'
I have to change font size of text 'Qty' in table with one row and one column, how can I make it?

You need to get the paragraph in the cell. From the documentation of python-docx:
3.5.2 _Cell objects:
class docx.table._Cell (tc, parent)
paragraphs
List of paragraphs in the cell. A table cell is required to
contain at least one block-level element and end with a paragraph. By
default, a new cell contains a single paragraph. Read-only
Reference: python-docx Documentation - Read the Docs
The code:
To change font size of text 'Qty'
paragraph =hdr_cells[0].paragraphs[0]
run = paragraph.runs
font = run[0].font
font.size= Pt(30) # font size = 30
To change font size of the whole table:
for row in table.rows:
for cell in row.cells:
paragraphs = cell.paragraphs
for paragraph in paragraphs:
for run in paragraph.runs:
font = run.font
font.size= Pt(30)
Reference of how to access paragraphs in a table: Extracting data from tables

Up there the solution really helped. I use it for a while. But I found a tiny problem:time. As your table grows bigger the time you cost to build the table getting longer. So I improve it. Cut two rounds. Here you are:
The code changes the whole table
for row in table.rows:
for cell in row.cells:
paragraphs = cell.paragraphs
paragraph = paragraphs[0]
run_obj = paragraph.runs
run = run_obj[0]
font = run.font
font.size = Pt(30)
When you cut two rounds you save the time

Building on user7609283 answer, here is a short version to set a cell to bold (the cell must contain text before applying format as it looks for the first paragraph):
row_cells = table.add_row().cells
row_cells[0].text = "Customer"
row_cells[0].paragraphs[0].runs[0].font.bold = True

This font change applies to all cells of a table and is streamlined. All cells must contain text before being formatted or an index out of range error is thrown:
for row in table.rows:
for cell in row.cells:
cp = cell.paragraphs[0].runs
cp[0].font.size = Pt(14)
This next font change applies to a single cell as wanted, in this case the top left cell:
tc = table.cell(0, 0).paragraphs[0].runs
tc[0].font.size = Pt(14)

Related

Is a font record index in BIFF8 zero-based?

I am programmatically reading and writing an Excel worksheet, using the source code from https://www.codeproject.com/articles/13852/basicexcel-a-class-to-read-and-write-to-microsoft, which in turn is based on the documentation of the Excel file format from http://sc.openoffice.org/excelfileformat.pdf.
The Excel file format supported by the source code is Binary Interchange File Format version 8 (BIFF8).
One of the records in an Excel file is an Extended Format (XF) record. The first two bytes of the XF record is an index to a FONT record. That is all the documentation has to say about it.
My question: Is that a zero-based index or a 1-based index?
The following is a use case that confused me and lead me to ponder this question.
Use case: bold cell
I created a simple Excel worksheet: one cell containing text that is bold.
I programmatically read that Excel worksheet and dump all the data to a human readable format, using new dump() methods I added to the source code. I find that:
My cell is associated with a LABELSST record: <LabelSST rowIdx=0 colIdx=0 xfRecIdx=62 sstRecIdx=0 />
That LABELSST record refers to an XF record with an index of: 62
If that is a zero-based index, the XF record at index 62 is: <XF fontRecIdx=20 formatRecIdx=0 protect=0x1 align=0x20 rot=0 text=0 usedAttribs=0x8 borderLines=0 color1=0x2000000 color2=0x20c0 />
That XF record refers to a FONT record with an index of: 20
If that is a zero-based index, the FONT record at index 20 is: <Font height=220 options=0 colorIdx=9 weight=400 escType=0 uline=0 family=2 charSet=0 name="Calibri" />
That FONT record has a weight of 400.
That font weight of 400 is not what I expected. If my cell content is bold, then the font weight should be 700, as per the documentation.
However, if the XF record refers to a FONT record with a 1-based index, then FONT record at 1-based index 20 is: <Font height=220 options=1 colorIdx=8 weight=700 escType=0 uline=0 family=2 charSet=0 name="Calibri" />
And that FONT record does indeed have a weight of 700 to indicate bold.
This is confusing. I do not know if the index to FONT record in the XF record is supposed to be zero-based or 1-based.
I think I found the answer to my own question elsewhere in the http://sc.openoffice.org/excelfileformat.pdf documentation.
The font with index 4 is omitted in all BIFF versions. This means the first four fonts have zero-based indexes, and the fifth font and all following fonts are referenced with one-based indexes.
If that is true, then the data I observed makes more sense.

Python - webscraping; dictionary data structure

I need to scrape this website (http://setkab.go.id/profil-kabinet/#) and produce an Excel file that has headers "Cabinet names" in column 1 and "Era" in column 2. That means each Cabinet name (e.g. Kabinet Presidensil, Kabinet Sjahrir I) should have its own row - alongside its respective era (e.g. Era Revolusi Fisik, Era Republik Indonesia Serikat).
This is the closest I've gotten:
import requests
from bs4 import BeautifulSoup
response = requests.get('http://setkab.go.id/profil-kabinet/#')
soup = BeautifulSoup(response.text, 'html.parser')
eras = soup.find_all('div', attrs={'class':"wpb_accordion_section group"})
setkab = {}
for element in eras:
setkab[element.a.get_text()] = {}
for element in eras:
cabname = element.find('div',attrs={'class':'wpb_wrapper'}).get_text()
setkab[element.a.get_text()]['cbnm'] = cabname
for item in setkab.keys():
print item + setkab[item]['cbnm']
import os, csv
os.chdir("/Users/mxcodes/Code")
with open("setkabfinal.csv", "w") as toWrite:
writer = csv.writer(toWrite, delimiter=",")
writer.writerow(["Era", "Cabinet name"])
for a in setkab.keys():
writer.writerow([a.encode("utf-8"), setkab[a]["cbnm"]])
However, this creates an Excel file with the headers "Era" and "Cabinet names" in column 1 and 2, respectively. It fails to put each Cabinet name in a separate row. For example, it has 'Era Revolusi Fisik' in column 1 and lists all the cabinets together in column 2.
My guess is that I need to switch the key-value pairs somehow so that each Cabinet becomes a key and its era becomes its value - because currently it's the other way around. But I've tried and failed to do so. Any help? Thank you!
From what I can see, the cabinets[a]["cbnm"] variable you use for writing is just a long Unicode so when you do writer.writerow([a.encode("utf-8"), cabinets[a]["cbnm"]]) what actually happens is that you write the era at the first column and the whole Unicode in the single cell in the next column (even if you have \n in your string it does not prevent it from being writed in a single cell (csv actually think that you want the unicode to be in ONLY one cell so it puts " before and after the cabinets[a]["cbnm"] value to be sure it will actually be in one cell)), what you should do to write every cabinet value in another row is to use the writerow method separately for each desired row.
for example this code worked fine for me:
cabinets = setkab
with open("cabinets.csv", "w") as toWrite:
writer = csv.writer(toWrite, delimiter=",")
writer.writerow(["Era", "Cabinet name"])
for a in setkab.keys():
writer.writerow([a.encode("utf-8")]) #write the era column
cabinets_list = [i for i in cabinets[a]["cbnm"].split('\n') if i != ''] #get all the values that are separated by newline chars (if they aren't empty strings)
for i in cabinets_list: writer.writerow([a.encode("utf-8"),i]) #write every value separately in the CABINET NAME row
as you can see I changed only the last 3 lines.
I hope this will help you!

How do I set different column widths for each column of a tktable?

I have a table made using Python 2.7 and tktable v1.1 that looks like the following:
class GUI (Tkinter.Tk):
self.testTable = tktable.Table(self, rows = 30, cols = 30, state='disabled',titlecols=1,titlerows=1, \
selectmode='extended', variable=self.tktableArray, selecttype='row', colstretchmode='unset', \
maxwidth=500, maxheight=190, xscrollcommand = self.HScroll.set, yscrollcommand = self.VScroll.set) # Create the results table
self.testTable.grid(column= 2, row = 6, columnspan = 4) # Add results table to the grid
Irrelevant code was left out in order to not throw a wall of code up. My desire here is to size the column widths independently for each column. For instance in column 0 I have only 3 digit numbers and in column 1 I have a 10 character word. I know that I could use
self.testTable.configure(colwidth=10)
to set the widths of the columns but that applies to all columns. Is there a way to do this on a per-column basis? And even better, is there a way to make the column widths fit to the contents of the column? Any help is appreciated.
I've never used a tktable, but a quick read of the tktable documentation shows there's a width() method on the table object. Have you tried that?
# set width of column 0 to 3, column 1 to 10
self.testTable.width(0,3,1,10)
The right answer is:
columnwidth={'0':7,'1':12,'2':20,'3':35,'4':15,'5':15,'6':22}
self.table.width(**columnwidth)

In MS Excel, Text Width of text in a cell returned by GetTextExtentPoint32W is more than the actual width

I'm using GetTextExtentPoint32W to get width of a text in a cell in MS Excel 2010. The cell width is fetched using ActiveCell.Width. These two widths are then compared to determine whether the text fits in the cell or extends out of the cell.
Visually, even though the text fits perfectly in the cell, the text width returned by the method is more than the cell width. Also, when I increase the font size the difference between actual text width and that returned by the method increases.
Following is a part of the source code used to achieve the result. Please help me solve this error.
hDC = ctypes.windll.user32.GetDC(self.windowHandle)
tempBMP = ctypes.windll.gdi32.CreateCompatibleBitmap(hDC, 1, 1)
hBMP = ctypes.windll.gdi32.SelectObject(hDC, tempBMP)
iFontSize = self.excelCellObject.Font.Size
deviceCaps = ctypes.windll.gdi32.GetDeviceCaps(hDC, 90)
iFontSize = int(iFontSize)
iFontSize = ctypes.c_int(iFontSize)
iFontSize = ctypes.windll.kernel32.MulDiv(iFontSize, deviceCaps, 72)
iFontSize = iFontSize * -1
iFontWeight = 700 if self.excelCellObject.Font.Bold else 400
sFontName = self.excelCellObject.Font.Name
sFontItalic = self.excelCellObject.Font.Italic
sFontUnderline = True if self.excelCellObject.Font.Underline else False
sFontStrikeThrough = self.excelCellObject.Font.Strikethrough
#Create a font object with the correct size, weight and style
hFont = ctypes.windll.gdi32.CreateFontW(iFontSize,
0, 0, 0,
iFontWeight,
sFontItalic,
sFontUnderline,
sFontStrikeThrough,
False, False, False,
False, False,
sFontName)
#Load the font into the device context, storing the original font object
hOldFont = ctypes.windll.gdi32.SelectObject(hDC, hFont)
sText = self.excelCellObject.Text
log.io("\nText \t"+sText+"\n")
textLength = len(sText)
class structText(ctypes.Structure):
_fields_ = [("width", ctypes.c_int),
("height",ctypes.c_int)]
StructText = structText()
getTextExtentPoint = ctypes.windll.gdi32.GetTextExtentPoint32W
getTextExtentPoint.argtypes = [ctypes.c_void_p,
ctypes.c_char_p,
ctypes.c_int,
ctypes.POINTER(structText)]
getTextExtentPoint.restype = ctypes.c_int
#Get the text dimensions
a = ctypes.windll.gdi32.GetTextExtentPoint32W(hDC,
sText,
textLength,
ctypes.byref(StructText))
#Delete the font object we created
a = ctypes.windll.gdi32.DeleteObject(hFont)
a = ctypes.windll.gdi32.DeleteObject(tempBMP)
#Release the device context
a = ctypes.windll.user32.ReleaseDC(self.windowHandle, hDC)
textWidth = StructText.width
cellWidth = self.excelCellObject.Width
Thanks.
I do not use Python or Excel 2010 so cannot comment on your current approach. However, I have struggled with a similar problem. I hope the following points will be helpful.
Background
If you hover over the right boundary of an Excel column and hold the left mouse button you will get a display of the format: “Width: n.nn (mm pixels)”.
The help for the ColumnWidth property says:
One unit of column width is equal to the width of one character in the
Normal style. For proportional fonts, the width of the character 0
(zero) is used.
Use the Width property to return the width of a column in points.
As far as I can tell, “Normal style” means the standard font name and size at the time the workbook was created. Changing the standard font name and size for an existing workbook does not appear to have any effect. Changing the font name and size for a worksheet has no effect.
Two example displays for a standard width column are:
For Arial 10 Width: 8.43 (64 pixels)
For Tahoma 10.5 Width: 8.38 (72 pixels)
I have created a string of zeros and attempted to measure how many are visible depending on the width of the column. I found the number of zeroes that I could see matched the value displayed reasonably well for such as subjective measure.
With VBA, the ColumnWidth property of a column or cell sets or returns the width in characters.
With VBA, The read only Width property of a column or cell returns .75 * the width in pixels.
The significance of the above information is that the width values obtained from Excel are not necessarily correct for the font being used.
My problem and the solution I discovered
The problem I had was that I was merging cells and filling them with text. Although Excel will adjust the height of a row so the text within an unmerged cell is visible, it will not do so for a merged cell. I tried many techniques including Microsoft’s .Net, text rendering routines without success. Nothing I tried would reliably emulate Excel’s system for determining the width of text.
The technique I eventually used successfully involved picking a cell to the right and below all used cells for experimental use. I adjusted the width of the experimental cell’s column to match the combined width of the merged cell and copied the formatted value from the cell whose height I wanted. Because the experimental cell was unmerged, Excel would adjust the height as appropriate. I then made sure the source row was at least this height.
The key feature of this technique was that I was not trying to emulate Excel’s system for determining the width of text; I was using Excel’s system.
You would need to try different column widths for the experimental cell. I would start with the current width of the source column. If the row height matches that for a single row, the width of the source column is already sufficient. If the row height is more than that for a single row, I would increase the column width until the row height matched that for a single row and then adjust the source column’s width to match. If you start with the widest value (in characters or Python’s text width) you will probably get the source column width correct at the first attempt which would avoid the need for later adjustments.

Prawn Adding new line in table

I have the following code to build PDF document with Prawn:
items = [["PERIOD","EMPLOYEE", "EMPLOYEE NAME", "HOURS", "FTES"]]
items += #mandates.each.map do |mandate|
[
mandate[:fte_period_end_date],
mandate[:fte_employee_id],
strname,
mandate[:fte_sum_of_hours],
mandate[:fte_sum_of_ftes],
]
end
#mandates is sorted by fte_employee_id and fte_by period_end_date
I want to insert totals lines per employe for fte_sum_of_hours and fte_sum_of_ftes when pass throw next employee.
What command permits me to insert these lines with Prawn?
Pass them in the array that you are displaying the total from - in Ruby, calculate for each section of elements, the total. Don't do the work in Prawn (it's not Excel).
data = [["product 1: ","$10.00"],["product 2: ", "$20.00"],["Subtotal:","$30.00]]
For example. Then you can format the table with consideration to row 3, the subtotal, with cell styles.