Skip to content

Is there any support for rowspan and colspan in addHTML ? #1643

@rameshsomepallidrg

Description

@rameshsomepallidrg

We are not able to add Html tables with rowspan using Html::addHtml().

After generating content into docx Table Rows and collspans are not rendering properly .

Please find the below screen shot that actually breaking the table.
Screenshot 2019-06-13 at 2 35 31 PM

Please find the attached below screen shot for Original content.
Screenshot 2019-06-19 at 4 15 43 PM

Any help on this!

Thanks,
Ramesh S

Activity

danilocarva9

danilocarva9 commented on Aug 23, 2019

@danilocarva9

Any news? I'm having the same issue.

0b10011

0b10011 commented on Aug 30, 2019

@0b10011
Contributor

It looks like colspan is recognized by the HTML reader, but rowspan is not. I took a quick look, and it's a little trickier mapping rowspan to PHPWord because HTML uses a number (like colspan), but PHPWord has a flag on cells to mark whether it's part of a rowspan or not. It requires a bit more state tracking between rows, so it appears it was skipped. I'm waiting on #1669 to merge, but then there's a good chance I'll be able to tackle a fix for this.

simogeo

simogeo commented on Feb 10, 2022

@simogeo

Any news regarding rowspan support ? Would be worth value. Thanks

yherus

yherus commented on Feb 17, 2022

@yherus

please add this code at phpoffice/phpword/src/PhpWord/Shared/Html.php

protected static function parseCell($node, $element, &$styles)
    {
        $cellStyles = self::recursiveParseStylesInHierarchy($node, $styles['cell']);

        $colspan = $node->getAttribute('colspan');
        if (!empty($colspan)) {
            $cellStyles['gridSpan'] = $colspan - 0;
        }
        
        /** NEW **/
        $rowspan = $node->getAttribute('rowspan');
        if (!empty($rowspan)) {
            $cellStyles['vMerge'] = 'restart';
        }
        $beforespan = $node->getAttribute('beforespan');
        if (!empty($beforespan)) {
            $cellRowContinue = array('vMerge' => 'continue');
            $beforecolspan = $node->getAttribute('beforecolspan');
            if( ! empty($beforecolspan) ) $cellRowContinue['gridSpan'] = $beforecolspan;
            for($s=1;$s<=$beforespan;$s++){
                $element->addCell(null,$cellRowContinue);
            }
        }
     /*** END **/

        // set cell width to control column widths
        $width = isset($cellStyles['width']) ? $cellStyles['width'] : null;
        unset($cellStyles['width']); // would not apply
        $cell = $element->addCell($width, $cellStyles);

        if (self::shouldAddTextRun($node)) {
            return $cell->addTextRun(self::filterOutNonInheritedStyles(self::parseInlineStyle($node, $styles['paragraph'])));
        }

        return $cell;
    }

Code HTML

$html  = '<table style="width: 100%; border: 1px #000000 solid;" cellspacing="0" collpadding="0">
           <thead>
               <tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; ">
                   <th style="text-align:center;">A</th>
                   <th style="text-align:center;">B</th>
                   <th style="text-align:center;">C</th>
               </tr>
           </thead>
           <tbody>
               <tr><td > A1 </td><td colspan="2"> BC1 </td></tr>
               <tr><td rowspan="2" colspan="2"> AB23 </td><td> C2 </td></tr>
               <tr><td beforespan="1" beforecolspan="2" > C3 </td></tr>
               <tr><td rowspan="3" > A456 </td><td> B4 </td><td> C4 </td></tr>
               <tr><td rowspan="2" beforespan="1">B5</td><td>C5</td></tr>
               <tr><td beforespan="2">C6</td></tr>
               <tr><td> A7 </td><td> B7 </td><td> C7 </td></tr>
           </tbody>
        </table>';
        $Section = new \PhpOffice\PhpWord\Element\Section(0);
        \PhpOffice\PhpWord\Shared\Html::addHtml($Section, $html);
        $document->setComplexBlock('tabel2',$Section->getElement(0)) ;

Output:
image

simogeo

simogeo commented on Feb 18, 2022

@simogeo

Hi @yherus : thanks for your proposal. I will try that soon.

And what about this ? #2163

Thanks again

yherus

yherus commented on Feb 19, 2022

@yherus

this is just a trick for my project,
update: phpoffice/phpword/src/PhpWord/Shared/Html.php

protected static function parseCell($node, $element, &$styles)
    {
        $cellStyles = self::recursiveParseStylesInHierarchy($node, $styles['cell']);

        $colspan = $node->getAttribute('colspan');
        if (!empty($colspan)) {
            $cellStyles['gridSpan'] = $colspan - 0;
        }

        $rowspan = $node->getAttribute('rowspan');
        if (!empty($rowspan)) {
            $cellStyles['vMerge'] = 'restart';
        }
        $beforespan = $node->getAttribute('beforespan');
        if (!empty($beforespan)) {
            $cellRowContinue = array('vMerge' => 'continue');
            $beforecolspan = $node->getAttribute('beforecolspan');
            if( ! empty($beforecolspan) ) $cellRowContinue['gridSpan'] = $beforecolspan;
            for($s=1;$s<=$beforespan;$s++){
                $element->addCell(null,$cellRowContinue);
            }
        }

        // set cell width to control column widths
        $width = isset($cellStyles['width']) ? $cellStyles['width'] : null;
        unset($cellStyles['width']); // would not apply
        $cell = $element->addCell($width, $cellStyles);

        $afterspan = $node->getAttribute('afterspan');
        if (!empty($afterspan)) {
            $cellRowContinue = array('vMerge' => 'continue');
            $aftercolspan = $node->getAttribute('aftercolspan');
            if( ! empty($aftercolspan) ) $cellRowContinue['gridSpan'] = $aftercolspan;
            for($s=1;$s<=$afterspan;$s++){
                $element->addCell(null,$cellRowContinue);
            }
        }

        if (self::shouldAddTextRun($node)) {
            return $cell->addTextRun(self::filterOutNonInheritedStyles(self::parseInlineStyle($node, $styles['paragraph'])));
        }

        return $cell;
    }

Code html:

<table style="width: 100%; border: 1px #000000 solid;" cellspacing="0" collpadding="0">
            <thead>
                <tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; ">
                    <th style="text-align:center;">A</th>
                    <th style="text-align:center;">B</th>
                    <th style="text-align:center;">C</th>
                    <th style="text-align:center;">D</th>
                </tr>
            </thead>
            <tbody>
                <tr><td > A1 </td><td colspan="2"> BC1 </td><td> D1 </td></tr>
                <tr><td rowspan="2" colspan="2"> AB23 </td><td> C2 </td><td> D2 </td></tr>
                <tr><td beforespan="1" beforecolspan="2" > C3 </td><td> D3 </td></tr>
                <tr><td rowspan="3" > A456 </td><td> B4 </td><td rowspan="2" colspan="2"> CD45 </td></tr>
                <tr><td rowspan="2" beforespan="1" afterspan="1" aftercolspan="2">B5</td></tr>
                <tr><td beforespan="2">C6</td><td> D6 </td></tr>
                <tr><td> A7 </td><td> B7 </td><td> C7 </td><td> D7 </td></tr>
                <tr><td > A8 </td><td colspan="2"> BC8 </td><td > D8 </td></tr>
                <tr><td colspan="3"> ABC9 </td><td rowspan="2"> D9 </td></tr>
                <tr><td > A9 </td><td > B9 </td><td afterspan="1"> C9 </td></tr>
            </tbody>
         </table>

Output:
image

simogeo

simogeo commented on Feb 19, 2022

@simogeo

Thanks for sharing your piece of code but it does not fit my needs, because content is dynamic and only standards html tags and attributes will be available

linked a pull request that will close this issue on Aug 7, 2024
majeeed87

majeeed87 commented on Jun 4, 2025

@majeeed87

update the code: phpoffice/phpword/src/PhpWord/Shared/Html.php

add this variable at the beginning of Html class:

protected static $rowIndex = 0;

protected static $columnIndex = 0;

protected static $rowSpanArray = [];

update parseTable method:

protected static function parseTable($node, $element, &$styles)
{
    $elementStyles = self::parseInlineStyle($node, $styles['table']);

    $newElement = $element->addTable($elementStyles);

    // Add style name from CSS Class
    if (isset($elementStyles['className'])) {
        $newElement->getStyle()->setStyleName($elementStyles['className']);
    }
	
	self::$rowIndex = 0;
	self::$rowSpanArray = [];

    $attributes = $node->attributes;
    if ($attributes->getNamedItem('border')) {
        $border = (int) $attributes->getNamedItem('border')->nodeValue;
        $newElement->getStyle()->setBorderSize(Converter::pixelToTwip($border));
    }

    return $newElement;
}

update parseRow method:

protected static function parseRow($node, $element, &$styles)
{
    $rowStyles = self::parseInlineStyle($node, $styles['row']);
    if ($node->parentNode->nodeName == 'thead') {
        $rowStyles['tblHeader'] = true;
    }

    // set cell height to control row heights
    $height = $rowStyles['height'] ?? null;
    unset($rowStyles['height']); // would not apply
	
	self::$columnIndex = 0;
	self::$rowIndex = self::$rowIndex + 1;

    return $element->addRow($height, $rowStyles);
}

update parseCell method:

protected static function parseCell($node, $element, &$styles)
{
	$cellStyles = self::recursiveParseStylesInHierarchy($node, $styles['cell']);
	$vMergeStyle = self::recursiveParseStylesInHierarchy($node, $styles['cell']);
	self::$columnIndex = self::$columnIndex + 1;
	$search_row_items = ["rowIndex" => self::$rowIndex];
	$rowSpanCell = array_filter(self::$rowSpanArray, function ($item) use ($search_row_items) {
		return count(array_intersect_assoc($search_row_items, $item)) == count($search_row_items);
	});
	
	$colspan = $node->getAttribute('colspan');
	if (!empty($colspan)) {
		$cellStyles['gridSpan'] = $colspan - 0;
		self::$columnIndex = self::$columnIndex + $colspan - 1;
	}
	
	$rowspan = $node->getAttribute('rowspan');
	if (!empty($rowspan)) {
		$cellStyles['vMerge'] = 'restart';
		$gridSpan = 0;
		$colIndex = self::$columnIndex;
		if (!empty($colspan)){
			$gridSpan = $colspan;
			$colIndex = self::$columnIndex - $colspan + 1;
		}
		for ($x = 1; $x < $rowspan; $x++) {
		  array_push(self::$rowSpanArray, ['columnIndex'=>$colIndex, 'rowIndex'=>self::$rowIndex + $x, 'colspan'=>$gridSpan]);
		}
	}
	
	$search_column_item = ["columnIndex" => self::$columnIndex];
	$currentColumnRowSpan = array_filter($rowSpanCell, function ($item) use ($search_column_item) {
		return count(array_intersect_assoc($search_column_item, $item)) == count($search_column_item);
	});
	
	// set cell width to control column widths
	$width = $cellStyles['width'] ?? null;
	unset($cellStyles['width']); // would not apply
	$loop_check = self::$columnIndex;
	if (count($currentColumnRowSpan) == 0){
		$cell = $element->addCell($width, $cellStyles);
		$loop_check = self::$columnIndex + 1;
	}
	
	if (count($rowSpanCell) > 0) {
		unset($vMergeStyle['width']);
		foreach($rowSpanCell as $row) {
			if($row['columnIndex'] == $loop_check){
				$loop_check = $row['columnIndex'] + 1;
				
				if ($row['colspan'] > 0) {
					$vMergeStyle['gridSpan'] = $row['colspan'];
					self::$columnIndex = self::$columnIndex + $row['colspan'] + 1;
				} else {
					unset($vMergeStyle['gridSpan']);
					self::$columnIndex = self::$columnIndex + 1;
				}
				$vMergeStyle['vMerge'] = 'continue';
				$element->addCell($width, $vMergeStyle);
			}
		}
	}
	
	if (count($currentColumnRowSpan) > 0){
		$cell = $element->addCell($width, $cellStyles);
	}		
	
	$search_item = ["columnIndex" => self::$columnIndex + 1];
	$nextColumnRowSpan = array_filter($rowSpanCell, function ($item) use ($search_item) {
		return count(array_intersect_assoc($search_item, $item)) == count($search_item);
	});
	
	if (count($nextColumnRowSpan) > 0) {
		unset($vMergeStyle['width']);
		$loop_check = self::$columnIndex + 1;
		foreach($rowSpanCell as $row) {
			if($row['columnIndex'] == $loop_check){
				$loop_check = $row['columnIndex'] + 1;
				if ($row['colspan'] > 0) {
					$vMergeStyle['gridSpan'] = $row['colspan'];
					self::$columnIndex = self::$columnIndex + $row['colspan'] + 1;
				} else {
					unset($vMergeStyle['gridSpan']);
					self::$columnIndex = self::$columnIndex + 1;
				}
				$vMergeStyle['vMerge'] = 'continue';
				$element->addCell($width, $vMergeStyle);
			}
		}
	}
	
	if (self::shouldAddTextRun($node)) {
		return $cell->addTextRun(self::filterOutNonInheritedStyles(self::parseInlineStyle($node, $styles['paragraph'])));
	}

	return $cell;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @simogeo@0b10011@yherus@majeeed87@danilocarva9

      Issue actions

        Is there any support for rowspan and colspan in addHTML ? · Issue #1643 · PHPOffice/PHPWord