Skip to content

feat: add support for rowspan in addHTML() #2643

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
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

jnlin
Copy link
Contributor

@jnlin jnlin commented Aug 7, 2024

Description

This PR resolves #1643 . Thanks for @yherus for the initial code

Checklist:

  • My CI is 🟢
  • I have covered by unit tests my new code (check build/coverage for coverage report)
  • I have updated the documentation to describe the changes
  • I have updated the changelog

@coveralls
Copy link

coveralls commented Aug 7, 2024

Coverage Status

coverage: 97.181% (-0.03%) from 97.208%
when pulling da62475 on WritePath:rowspan
into f9ce804 on PHPOffice:master.

Copy link
Member

@Progi1984 Progi1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments

Html::addHtml($section, $html);

$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
self::assertTrue($doc->elementExists('/w:document/w:body/w:tbl'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jnlin Could you add more assertions for checking the colspan and the rowspan ?

@@ -6,6 +6,7 @@

- IOFactory : Added extractVariables method to extract variables from a document [@sibalonat](https://github.com/sibalonat) in [#2515](https://github.com/PHPOffice/PHPWord/pull/2515)
- PDF Writer : Documented how to specify a PDF renderer, when working with the PDF writer, as well as the three available choices by [@settermjd](https://github.com/settermjd) in [#2642](https://github.com/PHPOffice/PHPWord/pull/2642)
- HTML Parser : Added support for rowspan in add HTML [#1643](https://github.com/PHPOffice/PHPWord/issues/1643)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jnlin Could you add you as author ?

if (!empty($rowspan)) {
$cellStyles['vMerge'] = 'restart';
}
$beforespan = $node->getAttribute('beforespan');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This attribute doesn't exist. Can you use a Real attribute or an another method ?

$afterspan = $node->getAttribute('afterspan');
if (!empty($afterspan)) {
$cellRowContinue = ['vMerge' => 'continue'];
$aftercolspan = $node->getAttribute('aftercolspan');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This attribute doesn't exist. Can you use a Real attribute or an another method ?

@Progi1984 Progi1984 added the Status: Waiting for feedback Question has been asked, waiting for response from PR author label Aug 7, 2024
@arrack arrack deleted the rowspan branch March 7, 2025 05:30
@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 parseChildNodes method:

protected static function parseChildNodes($node, $element, $styles, $data): void
{
    if ('li' != $node->nodeName) {
        $cNodes = $node->childNodes;
        if (!empty($cNodes)) {
            foreach ($cNodes as $cNode) {
                if ($element instanceof AbstractContainer || $element instanceof Table || $element instanceof Row) {
                    if($cNode->nodeName == 'tr'){
			  self::$columnIndex = 0;
			  self::$rowIndex = self::$rowIndex + 1;
		  }
		  self::parseNode($cNode, $element, $styles, $data);
                }
            }
        }
    }
}

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]);
		}
    }
	
	$firstColumnRowSpan = self::arraySearch($rowSpanCell, ["columnIndex" => 1]);

    // set cell width to control column widths
    $width = $cellStyles['width'] ?? null;
    unset($cellStyles['width']); // would not apply
	if (count($firstColumnRowSpan) > 0){
		$loop_check = 1;
	} else {
		$cell = $element->addCell($width, $cellStyles);
		$loop_check = self::$columnIndex + 1;
	}
	
	if (count($rowSpanCell) > 0) {
		if($rowSpanCell[0]['columnIndex'] == $loop_check){
			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'];
					} else {
						unset($vMergeStyle['gridSpan']);
					}
					$vMergeStyle['vMerge'] = 'continue';
					$element->addCell($width, $vMergeStyle);
				}
			}
		}
	}
	
	if (count($firstColumnRowSpan) > 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
Status: Waiting for feedback Question has been asked, waiting for response from PR author
Development

Successfully merging this pull request may close these issues.

Is there any support for rowspan and colspan in addHTML ?
5 participants