Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting Specific Table Column Width #2571

Open
BrendonKoz opened this issue Feb 15, 2024 · 2 comments
Open

Setting Specific Table Column Width #2571

BrendonKoz opened this issue Feb 15, 2024 · 2 comments

Comments

@BrendonKoz
Copy link

BrendonKoz commented Feb 15, 2024

Describe the Bug

The last version I had used (v0.18) prior to the current release, seemed to handle the creation of columns (for/in Microsoft Word) that, based on the twip, seemed to correlate to the appropriate inches (via internal Microsoft Word ruler) when rendered. I only needed to assign a width to a single cell within the row for the column (as shown below, this is the TIME column), and the other columns would auto-fit the content.

For version 1.2.0, this functionality has seemingly regressed for rendering in Microsoft Word (2016). LibreOffice appears to render better, but still incorrectly. I believe this is because PhpWord is now explicitly assigning column sizes in the document.xml file.

image
Microsoft Word 2016 on PhpWord v0.18

image
Libre Office v24.2.0.3 on PhpWord v0.18

image
Microsoft Word 2016 on PhpWord v1.2.0

image
Libre Office v24.2.0.3 on PhpWord 1.2.0

In trying to track down the rendering differences between versions, I examined the uncompressed XML files to try to identify differences. The most glaring was in word/document.xml in the w:tblGrid definition. In version 0.18 the widths were only defined for the columns explicitly provided via PhpWord code/style, the other columns had no width attribute. This, however, had a detrimental impact on LibreOffice Writer. The updates may have caused a different result in rendering, and an unexpected one, but at least the rendering is the same in both editors. That said, attempting to modify the column width attributes and recompressing to a DOCX file did not provide any level of correction, so there must be a few other places where widths are defined.

For reference, in v0.18, the column's Preferred Width in Microsoft Word was defined as 1.39". For v1.2.0, it is still reporting a Preferred Width of 1.39"...but is very obviously being prevented from rendering that way, from something else.

Steps to Reproduce

Please provide a code sample that reproduces the issue.

<?php

require __DIR__.'/vendor/autoload.php';

// ... the FUN stuff !!!
$doc = new \PhpOffice\PhpWord\PhpWord();
\PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true);
$paper = new \PhpOffice\PhpWord\Style\Paper();
$paper->setSize('Letter');

// Set Styles
$sectionStyle = array(
    'marginTop'    => \PhpOffice\PhpWord\Shared\Converter::inchToTwip(.25),
    'marginRight'  => \PhpOffice\PhpWord\Shared\Converter::inchToTwip(.5),
    'marginBottom' => \PhpOffice\PhpWord\Shared\Converter::inchToTwip(.25),
    'marginLeft'   => \PhpOffice\PhpWord\Shared\Converter::inchToTwip(.5),
    'pageSizeW'    => $paper->getWidth(),
    'pageSizeH'    => $paper->getHeight()
);
$section = $doc->addSection($sectionStyle);
$header  = $section->addHeader();
$footer  = $section->addFooter();
$footer->addPreserveText('{PAGE} of {NUMPAGES}', null, array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER));
$tableStyle = array(
    'width'       => 100 * 50, // Word 2007 table width, in percentages, is measured in 50ths of a percent
    'unit'        => 'pct',
    'alignment'   => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER,
    'borderSize'  => 5,
    'borderColor' => '333333',
    'cellMargin'  => 60,
);
$doc->addTableStyle('reportTable', $tableStyle);
$dayCellStyle = array(
    'bgColor'     => '333333',
    'valign'      => 'center',
    'gridSpan'    => 5
);
$columnHeaderStyle = array(
    'bgColor'     => 'E1E1E1',
    'valign'      => 'center'
);
$columnTimeHeaderStyle = array(
    'bgColor'     => 'E1E1E1',
    'valign'      => 'center',
    'width'       => 2000
);
$dayCellTextStyle = 'dayCellTextStyle';
$doc->addFontStyle(
    $dayCellTextStyle, array(
        'size'       => 15,
        'bold'       => true,
        'align'      => 'center',
        'spaceAfter' => 0
    )
);
$stdCellStyle = array('valign' => 'center');
$stdCellTextStyle = 'stdCellTextStyle';
$doc->addFontStyle(
    $stdCellTextStyle, array(
        'size'       => 12,
        'align'      => 'center',
        'spaceAfter' => 0
    )
);
$boldCellTextStyle = 'boldCellTextStyle';
$doc->addFontStyle(
    $boldCellTextStyle, array(
        'size'       => 12,
        'align'      => 'center',
        'spaceAfter' => 0,
        'bold'       => true
    )
);
$columnHeaderTextStyle = 'columnHeaderTextStyle';
$doc->addFontStyle(
    $columnHeaderTextStyle, array(
        'size'       => 12,
        'bold'       => true,
        'allCaps'    => true,
        'align'      => 'center',
        'spaceAfter' => 0
    )
);
$paragraphStyle = array(
    'align' => 'center',
    'spaceAfter' => 0
);

// Create the table
$table = $section->addTable($tableStyle);
$table->addRow(null, array('tblHeader' => true));
$table->addCell(null, $dayCellStyle)->addText(date('l, n/j/Y', time()), $dayCellTextStyle, $paragraphStyle);

$table->addRow(null, array('tblHeader' => true));
// TABLE COLUMN HEADERS: | Location | Time | Event | Owner | Setup |
$table->addCell(null, $columnHeaderStyle)->addText('Location',     $columnHeaderTextStyle, $paragraphStyle);
$table->addCell(2000, $columnTimeHeaderStyle)->addText('Time',     $columnHeaderTextStyle, $paragraphStyle);
$table->addCell(null, $columnHeaderStyle)->addText('Event',        $columnHeaderTextStyle, $paragraphStyle);
$table->addCell(null, $columnHeaderStyle)->addText('Contact',      $columnHeaderTextStyle, $paragraphStyle);
$table->addCell(null, $columnHeaderStyle)->addText('Setup / Info', $columnHeaderTextStyle, $paragraphStyle);

$rooms = [
    'Community Room' => [
        1696366800 => [
            'booking_id' => 'cs_o7xaaBsJ',
            'owner' => 'User #1',
            'event' => 'Paint with Patrice Workshop',
            'open' => 1696366800,
            'close' => 1696379400,
            'event_id' => 10902028,
            'start' => 1696370400,
            'end' => 1696377600,
            'equipment' => 'Square Folding Table #4, Square Folding Table #3, Square Folding Table #2, Square Folding Table #1, Microphone (Cordless) #1',
            'setup' => '',
            'notes' => ''
        ],
        '1696352400' => [
            'booking_id' => 'cs_a55vjKS2',
            'owner' => 'User #2',
            'event' => 'Tech Run Through for Caffe Lena Presents 60 Years of Banned Books',
            'open' => 1696352400,
            'close' => 1696356000,
            'event_id' => 11086161,
            'start' => 1696352400,
            'end' => 1696356000,
            'equipment' => '',
            'setup' => '',
            'notes' => ''
        ],
        '1696341600' => [
            'booking_id' => 'cs_K2VPwAtp',
            'owner' => 'User #2',
            'event' => 'Mah Jongg Class',
            'open' => 1696341600,
            'close' => 1696347000,
            'event_id' => 10608804,
            'start' => 1696341600,
            'end' => 1696347000,
            'equipment' => '',
            'setup' => 'Set up 5 square tables with 4 chairs each.',
            'notes' => ''
        ]
    ]
];

foreach ($rooms as $room => $booking) {
    ksort($booking);

    $firstRow = true;
    foreach ($booking as $open => $data) {
        $table->addRow(null, array('cantSplit' => true));

        // Add the first cell (room name)
        if (count($booking) > 1) {
            if ($firstRow) {
                $vMergeCellStyle = array_merge($stdCellStyle, array('vMerge' => 'restart'));
                $table->addCell(null, $vMergeCellStyle)->addText($room, $stdCellTextStyle, $paragraphStyle);
                $firstRow = false;
            } else {
                $table->addCell(null, array('vMerge' => 'continue'));
            }
        } else {
            $table->addCell(null, $stdCellStyle)->addText($room, $stdCellTextStyle, $paragraphStyle);
        }

        // Add the remaining cells
        // TIME
        if ($data['start']) {
            $timeCell = $table->addCell(null, $stdCellStyle);
            $timeCellText = $timeCell->addTextRun($paragraphStyle);
            $timeCellText->addText('RESERVED:', $boldCellTextStyle);
            $timeCellText->addTextBreak();
            $timeCellText->addText(date('g:ia', $data['open']) . '-' . date('g:ia', $data['close']), $stdCellTextStyle);
            $timeCellText->addTextBreak();
            $timeCellText->addText('EVENT:', $boldCellTextStyle);
            $timeCellText->addTextBreak();
            $timeCellText->addText(date('g:ia', $data['start']) . '-' . date('g:ia', $data['end']), $stdCellTextStyle);
        } else {
            $table->addCell(null, $stdCellStyle)->addText(date('g:ia', $data['open']) . '-' . date('g:ia', $data['close']), $stdCellTextStyle, $paragraphStyle);
        }
        // EVENT
        $table->addCell(null, $stdCellStyle)->addText($data['event'], $stdCellTextStyle, $paragraphStyle);
        // OWNER
        $table->addCell(null, $stdCellStyle)->addText($data['owner'], $stdCellTextStyle, $paragraphStyle);
        // SETUP (Equipment, Setup, and/or Notes)
        $setupCell = $table->addCell(null, $stdCellStyle);
        $setupCellText = $setupCell->addTextRun($paragraphStyle);
        if ($data['equipment']) {
            $setupCellText->addText('EQUIPMENT: ', $boldCellTextStyle);
            $setupCellText->addText($data['equipment'], $stdCellTextStyle);
            if ($data['setup']) {
                $setupCellText->addTextBreak();
            }
        }
        if ($data['setup']) {
            $setup = explode("\n", $data['setup']);
            $setupCellText->addText('SETUP: ', $boldCellTextStyle);
            foreach ($setup as $index => $line) {
                if ($index !== 0) {
                    $setupCellText->addTextBreak();
                }
                $setupCellText->addText($line, $stdCellTextStyle);
            }
        }
        if ($data['notes']) {
            $setup = explode("\n", $data['notes']);
            $text = $data['equipment'] ? ' NOTES: ' : 'NOTES: ';
            $setupCellText->addText($text, $boldCellTextStyle);
            foreach ($setup as $index => $line) {
                if ($index !== 0) {
                    $setupCellText->addTextBreak();
                }
                $setupCellText->addText($line, $stdCellTextStyle);
            }
        }

    }
}

// Set the header information based on data accumulated above
$room_text = 'Header Text';
$week_text = 'Week Text';
$header->addText($room_text . "\n", array('size' => 11), $paragraphStyle);
$header->addText($week_text, array('size' => 11, 'bold' => true), array('align' => 'center'));

// Configure metadata
$metadata = $doc->getDocInfo();
$metadata->setCreator('BrendonKoz');
$metadata->setTitle('PHPWord Test');
$metadata->setDescription('Rendered test document');
$metadata->setCreated(time());
$metadata->setModified(time());

// Create the Word Doc
header("Content-Description: File Transfer");
header('Content-Disposition: attachment; filename="Untitled.docx"');
header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Expires: 0');
$xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($doc, 'Word2007');
$xmlWriter->save("php:https://output");

Expected Behavior

The expectation was that the 2000 twip units on the TIME column would've been set to the converted 1.39" column width, and the other columns would've auto-fit.

Current Behavior

The preferred width is set, but it seems that the other columns are overriding widths in some sort of calculated width...

Context

Please fill in your environment information:

  • PHP Version: 8.2
  • PHPWord Version: 1.2.0
@imarakhovets
Copy link

You need to use layout style for table.

layout is LAYOUT_FIXED

$fancyTableStyle = [
    'borderSize'  => 6,
    'borderColor' => '000000',
    'cellMargin'  => 80,
    'alignment'   => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER,
    'layout'      => \PhpOffice\PhpWord\Style\Table::LAYOUT_FIXED,
];
$table = $section->addTable($fancyTableStyle);

@BrendonKoz
Copy link
Author

BrendonKoz commented Aug 1, 2024

I still happened to have this setup in a folder, so tested this adjustment. As I thought, it still does not work in a similar manner to v0.18. The expectation here is that I can configure a single column's width, but allow the other columns to autofit as necessary. Once an entire table has the setting applied for LAYOUT_FIXED, it seems like all columns expect a custom width to be applied, or there will be indeterminate sizes applied. Even with the TIME column width explicitly set to a preferred width of 1.39", it can be clearly seen below that the width is indeed much larger than 1.39" after applying the LAYOUT_FIXED attribute to the table, as suggested above.

I will need to update to the most recent version and see if this issue still applies under either layout setting for a table.

image

image

NOTE: Images above taken from Microsoft Word 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants