Skip to content

Parse formatting inside HTML lists #1239

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

Merged
merged 12 commits into from
Jan 13, 2018
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ v0.15.0 (?? ??? 2018)
----------------------
### Added
- Parsing of "align" HTML attribute - @troosan #1231
- Parse formatting inside HTML lists - @troosan @samimussbach #1239 #945 #1215 #508

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF), HTML, and PDF.

PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/develop/).
PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/).

If you have any questions, please ask on [StackOverFlow](https://stackoverflow.com/questions/tagged/phpword)

Expand Down
4 changes: 3 additions & 1 deletion docs/elements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,10 @@ For instance for the INDEX field, you can do the following (See `Index Field for
$fieldText->addText('My ');
$fieldText->addText('bold index', ['bold' => true]);
$fieldText->addText(' entry');
$section->addField('XE', array(), array(), $fieldText);

$section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), $fieldText);
//this actually adds the index
$section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), 'right click to update index');

Line
----
Expand Down
37 changes: 31 additions & 6 deletions samples/Sample_26_Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,50 @@
$html = '<h1>Adding element via HTML</h1>';
$html .= '<p>Some well formed HTML snippet needs to be used</p>';
$html .= '<p>With for example <strong>some<sup>1</sup> <em>inline</em> formatting</strong><sub>1</sub></p>';
$html .= '<p>Unordered (bulleted) list:</p>';

$html .= '<p style="margin-top: 240pt;">Unordered (bulleted) list:</p>';
$html .= '<ul><li>Item 1</li><li>Item 2</li><ul><li>Item 2.1</li><li>Item 2.1</li></ul></ul>';
$html .= '<p>Ordered (numbered) list:</p>';
$html .= '<ol><li>Item 1</li><li>Item 2</li></ol>';

$html .= '<p>List with complex content:</p>';
$html .= '<p style="margin-top: 240pt;">Ordered (numbered) list:</p>';
$html .= '<ol>
<li><p style="font-weight: bold;">List 1 item 1</p></li>
<li>List 1 item 2</li>
<ol>
<li>sub list 1</li>
<li>sub list 2</li>
</ol>
<li>List 1 item 3</li>
</ol>
<p style="margin-top: 15px;">A second list, numbering should restart</p>
<ol>
<li>List 2 item 1</li>
<li>List 2 item 2</li>
<ol>
<li>sub list 1</li>
<li>sub list 2</li>
</ol>
<li>List 2 item 3</li>
<ol>
<li>sub list 1, restarts with a</li>
<li>sub list 2</li>
</ol>
</ol>';

$html .= '<p style="margin-top: 240pt;">List with formatted content:</p>';
$html .= '<ul>
<li>
<span style="font-family: arial,helvetica,sans-serif;">
<span style="font-size: 12px;">list item1</span>
<span style="font-size: 16px;">big list item1</span>
</span>
</li>
<li>
<span style="font-family: arial,helvetica,sans-serif;">
<span style="font-size: 12px;">list item2</span>
<span style="font-size: 10px; font-weight: bold;">list item2 in bold</span>
</span>
</li>
</ul>';

$html .= '<p style="margin-top: 240pt;">A table with formatting:</p>';
$html .= '<table align="center" style="width: 50%; border: 6px #0000FF double;">
<thead>
<tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; ">
Expand Down
7 changes: 4 additions & 3 deletions src/PhpWord/Element/AbstractContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@
* @method CheckBox addCheckBox(string $name, $text, mixed $fStyle = null, mixed $pStyle = null)
* @method Title addTitle(string $text, int $depth = 1)
* @method TOC addTOC(mixed $fontStyle = null, mixed $tocStyle = null, int $minDepth = 1, int $maxDepth = 9)
*
* @method PageBreak addPageBreak()
* @method Table addTable(mixed $style = null)
* @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false)
* @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null)
* @method OLEObject addOLEObject(string $source, mixed $style = null)
* @method TextBox addTextBox(mixed $style = null)
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
* @method Line addLine(mixed $lineStyle = null)
Expand All @@ -46,6 +45,8 @@
* @method FormField addFormField(string $type, mixed $fStyle = null, mixed $pStyle = null)
* @method SDT addSDT(string $type)
*
* @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null) deprecated, use addOLEObject instead
*
* @since 0.10.0
*/
abstract class AbstractContainer extends AbstractElement
Expand Down Expand Up @@ -200,7 +201,7 @@ private function checkValidity($method)
'FormField' => $generalContainers,
'SDT' => $generalContainers,
'TrackChange' => $generalContainers,
'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox', 'TrackChange'),
'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox', 'TrackChange', 'ListItemRun'),
'ListItem' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
'ListItemRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
'Table' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
Expand Down
75 changes: 57 additions & 18 deletions src/PhpWord/Shared/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
namespace PhpOffice\PhpWord\Shared;

use PhpOffice\PhpWord\Element\AbstractContainer;
use PhpOffice\PhpWord\Element\Cell;
use PhpOffice\PhpWord\Element\Row;
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\SimpleType\NumberFormat;

/**
* Common Html functions
Expand All @@ -30,6 +30,8 @@
*/
class Html
{
private static $listIndex = 0;

/**
* Add HTML parts.
*
Expand Down Expand Up @@ -135,8 +137,8 @@ protected static function parseNode($node, $element, $styles = array(), $data =
'tr' => array('Row', $node, $element, $styles, null, null, null),
'td' => array('Cell', $node, $element, $styles, null, null, null),
'th' => array('Cell', $node, $element, $styles, null, null, null),
'ul' => array('List', null, null, $styles, $data, 3, null),
'ol' => array('List', null, null, $styles, $data, 7, null),
'ul' => array('List', $node, $element, $styles, $data, null, null),
'ol' => array('List', $node, $element, $styles, $data, null, null),
'li' => array('ListItem', $node, $element, $styles, $data, null, null),
'img' => array('Image', $node, $element, $styles, null, null, null),
'br' => array('LineBreak', null, $element, $styles, null, null, null),
Expand Down Expand Up @@ -330,7 +332,7 @@ private static function parseRow($node, $element, &$styles)
* @param \DOMNode $node
* @param \PhpOffice\PhpWord\Element\Table $element
* @param array &$styles
* @return Cell $element
* @return \PhpOffice\PhpWord\Element\Cell $element
*/
private static function parseCell($node, $element, &$styles)
{
Expand Down Expand Up @@ -365,18 +367,56 @@ private static function recursiveParseStylesInHierarchy(\DOMNode $node, array $s
/**
* Parse list node
*
* @param \DOMNode $node
* @param \PhpOffice\PhpWord\Element\AbstractContainer $element
* @param array &$styles
* @param array &$data
* @param string $argument1 List type
*/
private static function parseList(&$styles, &$data, $argument1)
private static function parseList($node, $element, &$styles, &$data)
{
$isOrderedList = $node->nodeName == 'ol';
if (isset($data['listdepth'])) {
$data['listdepth']++;
} else {
$data['listdepth'] = 0;
$styles['list'] = 'listStyle_' . self::$listIndex++;
$element->getPhpWord()->addNumberingStyle($styles['list'], self::getListStyle($isOrderedList));
}
}

private static function getListStyle($isOrderedList)
{
if ($isOrderedList) {
return array(
'type' => 'multilevel',
'levels' => array(
array('format' => NumberFormat::DECIMAL, 'text' => '%1.', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360),
array('format' => NumberFormat::LOWER_LETTER, 'text' => '%2.', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360),
array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%3.', 'alignment' => 'right', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 180),
array('format' => NumberFormat::DECIMAL, 'text' => '%4.', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360),
array('format' => NumberFormat::LOWER_LETTER, 'text' => '%5.', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360),
array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%6.', 'alignment' => 'right', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 180),
array('format' => NumberFormat::DECIMAL, 'text' => '%7.', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360),
array('format' => NumberFormat::LOWER_LETTER, 'text' => '%8.', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360),
array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%9.', 'alignment' => 'right', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 180),
),
);
}
$styles['list']['listType'] = $argument1;

return array(
'type' => 'hybridMultilevel',
'levels' => array(
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
),
);
}

/**
Expand All @@ -394,17 +434,10 @@ private static function parseListItem($node, $element, &$styles, $data)
{
$cNodes = $node->childNodes;
if (!empty($cNodes)) {
$text = '';
$listRun = $element->addListItemRun($data['listdepth'], $styles['list'], $styles['paragraph']);
foreach ($cNodes as $cNode) {
if ($cNode->nodeName == '#text') {
$text = $cNode->nodeValue;
}
self::parseNode($cNode, $listRun, $styles, $data);
}
//ideally we should be parsing child nodes for any style, for now just take the text
if ('' == trim($text) && '' != trim($node->textContent)) {
$text = trim($node->textContent);
}
$element->addListItem($text, $data['listdepth'], $styles['font'], $styles['list'], $styles['paragraph']);
}
}

Expand Down Expand Up @@ -462,6 +495,12 @@ private static function parseStyle($attribute, $styles)
}
$styles['italic'] = $tValue;
break;
case 'margin-top':
$styles['spaceBefore'] = Converter::cssToPoint($cValue);
break;
case 'margin-bottom':
$styles['spaceAfter'] = Converter::cssToPoint($cValue);
break;
case 'border-color':
$styles['color'] = trim($cValue, '#');
break;
Expand Down Expand Up @@ -582,14 +621,14 @@ private static function mapBorderStyle($cssBorderStyle)
private static function mapAlign($cssAlignment)
{
switch ($cssAlignment) {
case 'left':
return Jc::START;
case 'right':
return Jc::END;
case 'center':
return Jc::CENTER;
case 'justify':
return Jc::BOTH;
default:
return Jc::START;
}

return null;
Expand Down
Loading