Skip to content

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

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

Open
rameshsomepallidrg opened this issue Jun 19, 2019 · 8 comments · May be fixed by #2643
Open

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

rameshsomepallidrg opened this issue Jun 19, 2019 · 8 comments · May be fixed by #2643

Comments

@rameshsomepallidrg
Copy link

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

@danilocarva9
Copy link

Any news? I'm having the same issue.

@0b10011
Copy link
Contributor

0b10011 commented Aug 30, 2019

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
Copy link

simogeo commented Feb 10, 2022

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

@yherus
Copy link

yherus commented Feb 17, 2022

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
Copy link

simogeo commented Feb 18, 2022

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

And what about this ? #2163

Thanks again

@yherus
Copy link

yherus commented Feb 19, 2022

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
Copy link

simogeo commented Feb 19, 2022

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

@jnlin jnlin linked a pull request Aug 7, 2024 that will close this issue
4 tasks
@majeeed87
Copy link

majeeed87 commented Jun 4, 2025

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;

    $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_items = ["rowIndex" => self::$rowIndex];
	$rowSpanCell = self::arraySearch(self::$rowSpanArray, $search_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]);
		}
	}
	
	$currentColumnRowSpan = self::arraySearch($rowSpanCell, ["columnIndex" => self::$columnIndex]);

	// set cell width to control column widths
	$width = $cellStyles['width'] ?? null;
	unset($cellStyles['width']); // would not apply
	if (count($currentColumnRowSpan) > 0){
		$loop_check = self::$columnIndex;
	} else {
		$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(null, $vMergeStyle);
			}
		}
	}
	
	if (count($currentColumnRowSpan) > 0){
		$cell = $element->addCell($width, $cellStyles);
	}
	
	if (self::shouldAddTextRun($node)) {
		return $cell->addTextRun(self::filterOutNonInheritedStyles(self::parseInlineStyle($node, $styles['paragraph'])));
	}

	return $cell;
}

add arraySearch method:

// PHP program to search for multiple
// key=>value pairs in array

protected static function arraySearch($array, $search_list)
{
	// Create the result array
	$result = [];

	// Iterate over each array element
	foreach ($array as $key => $value) {
		// Iterate over each search condition
		foreach ($search_list as $k => $v) {
			// If the array element does not meet
			// the search condition then continue
			// to the next element
			if (!isset($value[$k]) || $value[$k] != $v) {
				// Skip two loops
				continue 2;
			}
		}

		// Append array element's key to the
		//result array
		$result[] = $value;
	}

	// Return result
	return $result;
}

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

Successfully merging a pull request may close this issue.

6 participants