python-openxml / python-docx

Create and modify Word documents with Python
MIT License
4.61k stars 1.13k forks source link

Column widths in MS Word #360

Open savaskoc opened 7 years ago

savaskoc commented 7 years ago

Hi,

I have following table in my document;

    table = self.add_table(rows=len(array), cols=4)
    table.columns[0].width = Cm(3)
    table.columns[1].width = Cm(11)
    table.columns[2].width = Cm(3)
    table.columns[3].width = Cm(3)

    for i, x in enumerate(array):
        table.cell(i, 0).text = 'Foo'
        table.cell(i, 1).text = 'Foo'
        table.cell(i, 2).text = 'Foo'
        table.cell(i, 2).paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT
        table.cell(i, 3).text = 'Foo'
        table.cell(i, 3).paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT

If I open the document in Apple Pages, all column widths are correct but not on MS Word. Am I missing something?

scanny commented 7 years ago

Interpretation of column widths varies between platforms (like LibreOffice, Google Docs, etc.)

If you set the width of each cell individually I think you'll get what you want across platforms.

--Steve

On Fri, Feb 3, 2017 at 1:48 PM, Savaş KOÇ notifications@github.com wrote:

Hi,

I have following table in my document;

table = self.add_table(rows=len(array), cols=4)
table.columns[0].width = Cm(3)
table.columns[1].width = Cm(11)
table.columns[2].width = Cm(3)
table.columns[3].width = Cm(3)

for i, x in enumerate(array):
    table.cell(i, 0).text = 'Foo'
    table.cell(i, 1).text = 'Foo'
    table.cell(i, 2).text = 'Foo'
    table.cell(i, 2).paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    table.cell(i, 3).text = 'Foo'
    table.cell(i, 3).paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT

If I open the document in Apple Pages, all column widths are correct but not on MS Word. Am I missing something?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/python-openxml/python-docx/issues/360, or mute the thread https://github.com/notifications/unsubscribe-auth/AB95fnhURHjTYLkZONhOimc3DRyO5aTwks5rY6DGgaJpZM4L22EA .

hMutzner commented 6 years ago

I did:

def set_column_width(table,column,width_mm):
    table.allow_autofit = False
    for row in table.rows:
        row.cells[column].width = Mm(width_mm)

and the cells in Word get about 7mm larger than specified in python. Setting the width of each cell individually does not seem to give correct widths in word.

scanny commented 6 years ago

I'm not sure if the column width includes the internal cell margin or not. That could possibly account for the difference.

hMutzner commented 6 years ago

Setting the cell margins to 0 (manually in MS Word in the original document) does not change anything. Left and right cell margins are 1.9mm, but MS Word adds about 7 mm to the cell width.

In the following test, everything works perfectly

from docx import Document
from docx.shared import Mm

def set_column_width(table,column,width_mm):
    table.allow_autofit = False
    for row in table.rows:
        row.cells[column].width = Mm(width_mm)

document = Document()
rows=4
cols=3

document.add_heading('Empty table - column 0 set to 25 mm width', level=1)
table = document.add_table(rows,cols)
set_column_width(table,0,25)

document.add_heading('Table with Text - column 0 set to 25 mm width',level=1)
table2=document.add_table(rows,cols)
for row in table2.rows:
    for cell in row.cells:
       cell.text=('here is some text')

set_column_width(table2,0,25)

document.save('C:/Users/      /tabletest.docx')`

In the original document may be some formatting waiting for discovery. I'm searching.

I read the original document, do some formatting with python-docx and write an output document. The column widths in the XML of the output document are correct. In dxa units.

Btw: this site is useful for unit conversion: http://lcorneliussen.de/raw/dashboards/ooxml/

hMutzner commented 6 years ago

Setting the widths of individual cells to 35 mm = 1984 dxa with python-docx gives:

    <w:tbl>
        <w:tblPr>
            <w:tblW w:w="9288" w:type="dxa"/>
            <w:tblBorders>
                <w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:insideH w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:insideV w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            </w:tblBorders>
            <w:tblLook w:val="0000" w:firstRow="0" w:lastRow="0" w:firstColumn="0" w:lastColumn="0" w:noHBand="0" w:noVBand="0"/>
        </w:tblPr>
        <w:tblGrid>
    --->        <w:gridCol w:w="3510"/> 
            <w:gridCol w:w="5778"/>
        </w:tblGrid>
        <w:tr w:rsidR="000A3B7A" w:rsidRPr="00046723" w:rsidTr="0089115F">
            <w:tc>
                <w:tcPr>
    --->                <w:tcW w:w="1984" w:type="dxa"/>
                    <w:shd w:val="clear" w:color="auto" w:fill="auto"/>
                </w:tcPr>
                <w:p>
                    <w:r>
                        <w:t>Arbeitsaufwand</w:t>
                    </w:r>
                </w:p>
            </w:tc>

Setting column width to 35 mm = 1984 dxa manually in MS-Word gives:

    <w:tbl>
        <w:tblPr>
            <w:tblW w:w="8898" w:type="dxa"/>
            <w:tblBorders>
                <w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:insideH w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                <w:insideV w:val="single" w:sz="4" w:space="0" w:color="auto"/>
            </w:tblBorders>
            <w:tblLook w:val="0000" w:firstRow="0" w:lastRow="0" w:firstColumn="0" w:lastColumn="0" w:noHBand="0" w:noVBand="0"/>
        </w:tblPr>
        <w:tblGrid>
--->            <w:gridCol w:w="1984"/>
            <w:gridCol w:w="6914"/>
        </w:tblGrid>
        <w:tr w:rsidR="000A3B7A" w:rsidRPr="00046723" w:rsidTr="00A24E8D">
            <w:tc>
                <w:tcPr>
--->                    <w:tcW w:w="1984" w:type="dxa"/>
                    <w:shd w:val="clear" w:color="auto" w:fill="auto"/>
                </w:tcPr>
                <w:p w:rsidR="009442E8" w:rsidRDefault="00877FA9">
                    <w:bookmarkStart w:id="0" w:name="_GoBack" w:colFirst="0" w:colLast="0"/>
                    <w:r>
                        <w:t>Arbeitsaufwand</w:t>
                    </w:r>
                </w:p>
            </w:tc>
            <w:tc>

Hence, I set both column width and individual cell widths in python-docx and get:

        <w:tbl>
            <w:tblPr>
                <w:tblW w:w="9288" w:type="dxa"/>
                <w:tblBorders>
                    <w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                    <w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                    <w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                    <w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                    <w:insideH w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                    <w:insideV w:val="single" w:sz="4" w:space="0" w:color="auto"/>
                </w:tblBorders>
                <w:tblLook w:val="0000" w:firstRow="0" w:lastRow="0" w:firstColumn="0" w:lastColumn="0" w:noHBand="0" w:noVBand="0"/>
            </w:tblPr>
            <w:tblGrid>
--->                <w:gridCol w:w="1984"/>
                <w:gridCol w:w="5778"/>
            </w:tblGrid>
            <w:tr w:rsidR="000A3B7A" w:rsidRPr="00046723" w:rsidTr="0089115F">
                <w:tc>
                    <w:tcPr>
--->                        <w:tcW w:w="1984" w:type="dxa"/>
                        <w:shd w:val="clear" w:color="auto" w:fill="auto"/>
                    </w:tcPr>
                    <w:p>
                        <w:r>
                            <w:t>Arbeitsaufwand</w:t>
                        </w:r>
                    </w:p>
                </w:tc>

But MS-Word still shows a column width of 41.6 mm. :-(

scanny commented 6 years ago

What about the table width (w:tblW w:w="9288" vs. 8898 for Word)?

Maybe Word is maintaining the proportion, but scaling to fit the table width.

I see there's not a Table.width property, but maybe that's where to look. This code might work for setting that:

tblPr = table._tblPr
tblW = tblPr.xpath('./tblW')[0]
tblW.width = Mm(42)  # --whatever the sum of column widths is.
caliraftdude commented 3 years ago

I spent a few hours trying to figure out what I was doing wrong here. At first, I was trying to set the column width, to no avail (page is landscape): block_table.columns[0].width = Inches(0.7) block_table.columns[1].width = Inches(0.7) block_table.columns[2].width = Inches(1.3) block_table.columns[3].width = Inches(7.8) block_table.autofit = False

The header (full code clip later for brevity) would look fine, but after adding data it would go to the columns begin equal width across the page. So I tried setting this after populating the data, same problem.

I then shifted to setting all of the header column cells the proper width: block_table.columns[0].cells[0].width = Inches(0.7) block_table.columns[1].cells[0].width = Inches(0.7) block_table.columns[2].cells[0].width = Inches(1.3) block_table.columns[3].cells[0].width = Inches(7.8) block_table.autofit = False

This also fails, in the same manner, either before or after populating data within the table.

The only way to get it to work was as suggested above to where you need to go to each column and then set each row's cell in that column to the desired width, otherwise it never works. I can see it updating the objects width in the debugger, so I know its happening to a degree.

This leads me to believe there might be a couple of issues here:

  1. able.columns[0].width doesn't properly set the width of ALL cells in a column
  2. When you add a row, it does NOT inherit the width property from the previous row and thus anytime you add data you with have columns of different sizes and Word probably hurls on this and just resizes all the columns.

Not sure if this helps, but the library is providing a lot of value for me right now and I felt it was appropriate to share some observations. Hopefully useful.

    # populate header row --------
    heading_cells = block_table.rows[0].cells
    heading_cells[0].text = 'ID'
    heading_cells[1].text = 'FIXED'
    heading_cells[2].text = 'WORKAROUND'
    heading_cells[3].text = 'TITLE'

    for data in example_data:
        cell_row = block_table.add_row().cells
        cell_row[0].text = data.get("ID")
        cell_row[1].text = data.get("FIXED")
        cell_row[2].text = data.get("WORKAROUND")
        cell_row[3].text = data.get("TITLE")

    self.setColumnWidth(block_table, 0, 0.7)
    self.setColumnWidth(block_table, 1, 0.7)
    self.setColumnWidth(block_table, 2, 1.3)
    self.setColumnWidth(block_table, 3, 7.8)
cgarmstr commented 8 months ago

Please add the recommendation to set cell rather than column widths to the documentation. I too wasted a lot of time constructing examples to figure out what was going wrong until I went looking is these pages.