Skip to content

Commit 4c68ebb

Browse files
mussbachtroosan
authored andcommitted
Parse formatting inside HTML lists (#1239)
1 parent 99b04f0 commit 4c68ebb

File tree

7 files changed

+169
-33
lines changed

7 files changed

+169
-33
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ v0.15.0 (?? ??? 2018)
77
----------------------
88
### Added
99
- Parsing of "align" HTML attribute - @troosan #1231
10+
- Parse formatting inside HTML lists - @troosan @samimussbach #1239 #945 #1215 #508
1011

1112
### Fixed
1213
- fix reading of docx default style - @troosan #1238

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
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.
1212

13-
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/).
13+
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/).
1414

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

docs/elements.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,10 @@ For instance for the INDEX field, you can do the following (See `Index Field for
405405
$fieldText->addText('My ');
406406
$fieldText->addText('bold index', ['bold' => true]);
407407
$fieldText->addText(' entry');
408+
$section->addField('XE', array(), array(), $fieldText);
408409
409-
$section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), $fieldText);
410+
//this actually adds the index
411+
$section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), 'right click to update index');
410412
411413
Line
412414
----

samples/Sample_26_Html.php

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,50 @@
99
$html = '<h1>Adding element via HTML</h1>';
1010
$html .= '<p>Some well formed HTML snippet needs to be used</p>';
1111
$html .= '<p>With for example <strong>some<sup>1</sup> <em>inline</em> formatting</strong><sub>1</sub></p>';
12-
$html .= '<p>Unordered (bulleted) list:</p>';
12+
13+
$html .= '<p style="margin-top: 240pt;">Unordered (bulleted) list:</p>';
1314
$html .= '<ul><li>Item 1</li><li>Item 2</li><ul><li>Item 2.1</li><li>Item 2.1</li></ul></ul>';
14-
$html .= '<p>Ordered (numbered) list:</p>';
15-
$html .= '<ol><li>Item 1</li><li>Item 2</li></ol>';
1615

17-
$html .= '<p>List with complex content:</p>';
16+
$html .= '<p style="margin-top: 240pt;">Ordered (numbered) list:</p>';
17+
$html .= '<ol>
18+
<li><p style="font-weight: bold;">List 1 item 1</p></li>
19+
<li>List 1 item 2</li>
20+
<ol>
21+
<li>sub list 1</li>
22+
<li>sub list 2</li>
23+
</ol>
24+
<li>List 1 item 3</li>
25+
</ol>
26+
<p style="margin-top: 15px;">A second list, numbering should restart</p>
27+
<ol>
28+
<li>List 2 item 1</li>
29+
<li>List 2 item 2</li>
30+
<ol>
31+
<li>sub list 1</li>
32+
<li>sub list 2</li>
33+
</ol>
34+
<li>List 2 item 3</li>
35+
<ol>
36+
<li>sub list 1, restarts with a</li>
37+
<li>sub list 2</li>
38+
</ol>
39+
</ol>';
40+
41+
$html .= '<p style="margin-top: 240pt;">List with formatted content:</p>';
1842
$html .= '<ul>
1943
<li>
2044
<span style="font-family: arial,helvetica,sans-serif;">
21-
<span style="font-size: 12px;">list item1</span>
45+
<span style="font-size: 16px;">big list item1</span>
2246
</span>
2347
</li>
2448
<li>
2549
<span style="font-family: arial,helvetica,sans-serif;">
26-
<span style="font-size: 12px;">list item2</span>
50+
<span style="font-size: 10px; font-weight: bold;">list item2 in bold</span>
2751
</span>
2852
</li>
2953
</ul>';
3054

55+
$html .= '<p style="margin-top: 240pt;">A table with formatting:</p>';
3156
$html .= '<table align="center" style="width: 50%; border: 6px #0000FF double;">
3257
<thead>
3358
<tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; ">

src/PhpWord/Element/AbstractContainer.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,10 @@
3333
* @method CheckBox addCheckBox(string $name, $text, mixed $fStyle = null, mixed $pStyle = null)
3434
* @method Title addTitle(string $text, int $depth = 1)
3535
* @method TOC addTOC(mixed $fontStyle = null, mixed $tocStyle = null, int $minDepth = 1, int $maxDepth = 9)
36-
*
3736
* @method PageBreak addPageBreak()
3837
* @method Table addTable(mixed $style = null)
3938
* @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false)
40-
* @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null)
39+
* @method OLEObject addOLEObject(string $source, mixed $style = null)
4140
* @method TextBox addTextBox(mixed $style = null)
4241
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
4342
* @method Line addLine(mixed $lineStyle = null)
@@ -46,6 +45,8 @@
4645
* @method FormField addFormField(string $type, mixed $fStyle = null, mixed $pStyle = null)
4746
* @method SDT addSDT(string $type)
4847
*
48+
* @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null) deprecated, use addOLEObject instead
49+
*
4950
* @since 0.10.0
5051
*/
5152
abstract class AbstractContainer extends AbstractElement
@@ -200,7 +201,7 @@ private function checkValidity($method)
200201
'FormField' => $generalContainers,
201202
'SDT' => $generalContainers,
202203
'TrackChange' => $generalContainers,
203-
'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox', 'TrackChange'),
204+
'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox', 'TrackChange', 'ListItemRun'),
204205
'ListItem' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
205206
'ListItemRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),
206207
'Table' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'),

src/PhpWord/Shared/Html.php

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
namespace PhpOffice\PhpWord\Shared;
1919

2020
use PhpOffice\PhpWord\Element\AbstractContainer;
21-
use PhpOffice\PhpWord\Element\Cell;
2221
use PhpOffice\PhpWord\Element\Row;
2322
use PhpOffice\PhpWord\Element\Table;
2423
use PhpOffice\PhpWord\SimpleType\Jc;
24+
use PhpOffice\PhpWord\SimpleType\NumberFormat;
2525

2626
/**
2727
* Common Html functions
@@ -30,6 +30,8 @@
3030
*/
3131
class Html
3232
{
33+
private static $listIndex = 0;
34+
3335
/**
3436
* Add HTML parts.
3537
*
@@ -135,8 +137,8 @@ protected static function parseNode($node, $element, $styles = array(), $data =
135137
'tr' => array('Row', $node, $element, $styles, null, null, null),
136138
'td' => array('Cell', $node, $element, $styles, null, null, null),
137139
'th' => array('Cell', $node, $element, $styles, null, null, null),
138-
'ul' => array('List', null, null, $styles, $data, 3, null),
139-
'ol' => array('List', null, null, $styles, $data, 7, null),
140+
'ul' => array('List', $node, $element, $styles, $data, null, null),
141+
'ol' => array('List', $node, $element, $styles, $data, null, null),
140142
'li' => array('ListItem', $node, $element, $styles, $data, null, null),
141143
'img' => array('Image', $node, $element, $styles, null, null, null),
142144
'br' => array('LineBreak', null, $element, $styles, null, null, null),
@@ -330,7 +332,7 @@ private static function parseRow($node, $element, &$styles)
330332
* @param \DOMNode $node
331333
* @param \PhpOffice\PhpWord\Element\Table $element
332334
* @param array &$styles
333-
* @return Cell $element
335+
* @return \PhpOffice\PhpWord\Element\Cell $element
334336
*/
335337
private static function parseCell($node, $element, &$styles)
336338
{
@@ -365,18 +367,56 @@ private static function recursiveParseStylesInHierarchy(\DOMNode $node, array $s
365367
/**
366368
* Parse list node
367369
*
370+
* @param \DOMNode $node
371+
* @param \PhpOffice\PhpWord\Element\AbstractContainer $element
368372
* @param array &$styles
369373
* @param array &$data
370-
* @param string $argument1 List type
371374
*/
372-
private static function parseList(&$styles, &$data, $argument1)
375+
private static function parseList($node, $element, &$styles, &$data)
373376
{
377+
$isOrderedList = $node->nodeName == 'ol';
374378
if (isset($data['listdepth'])) {
375379
$data['listdepth']++;
376380
} else {
377381
$data['listdepth'] = 0;
382+
$styles['list'] = 'listStyle_' . self::$listIndex++;
383+
$element->getPhpWord()->addNumberingStyle($styles['list'], self::getListStyle($isOrderedList));
384+
}
385+
}
386+
387+
private static function getListStyle($isOrderedList)
388+
{
389+
if ($isOrderedList) {
390+
return array(
391+
'type' => 'multilevel',
392+
'levels' => array(
393+
array('format' => NumberFormat::DECIMAL, 'text' => '%1.', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360),
394+
array('format' => NumberFormat::LOWER_LETTER, 'text' => '%2.', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360),
395+
array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%3.', 'alignment' => 'right', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 180),
396+
array('format' => NumberFormat::DECIMAL, 'text' => '%4.', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360),
397+
array('format' => NumberFormat::LOWER_LETTER, 'text' => '%5.', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360),
398+
array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%6.', 'alignment' => 'right', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 180),
399+
array('format' => NumberFormat::DECIMAL, 'text' => '%7.', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360),
400+
array('format' => NumberFormat::LOWER_LETTER, 'text' => '%8.', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360),
401+
array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%9.', 'alignment' => 'right', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 180),
402+
),
403+
);
378404
}
379-
$styles['list']['listType'] = $argument1;
405+
406+
return array(
407+
'type' => 'hybridMultilevel',
408+
'levels' => array(
409+
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
410+
array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
411+
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
412+
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
413+
array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
414+
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
415+
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'),
416+
array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'),
417+
array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'),
418+
),
419+
);
380420
}
381421

382422
/**
@@ -394,17 +434,10 @@ private static function parseListItem($node, $element, &$styles, $data)
394434
{
395435
$cNodes = $node->childNodes;
396436
if (!empty($cNodes)) {
397-
$text = '';
437+
$listRun = $element->addListItemRun($data['listdepth'], $styles['list'], $styles['paragraph']);
398438
foreach ($cNodes as $cNode) {
399-
if ($cNode->nodeName == '#text') {
400-
$text = $cNode->nodeValue;
401-
}
439+
self::parseNode($cNode, $listRun, $styles, $data);
402440
}
403-
//ideally we should be parsing child nodes for any style, for now just take the text
404-
if ('' == trim($text) && '' != trim($node->textContent)) {
405-
$text = trim($node->textContent);
406-
}
407-
$element->addListItem($text, $data['listdepth'], $styles['font'], $styles['list'], $styles['paragraph']);
408441
}
409442
}
410443

@@ -462,6 +495,12 @@ private static function parseStyle($attribute, $styles)
462495
}
463496
$styles['italic'] = $tValue;
464497
break;
498+
case 'margin-top':
499+
$styles['spaceBefore'] = Converter::cssToPoint($cValue);
500+
break;
501+
case 'margin-bottom':
502+
$styles['spaceAfter'] = Converter::cssToPoint($cValue);
503+
break;
465504
case 'border-color':
466505
$styles['color'] = trim($cValue, '#');
467506
break;
@@ -582,14 +621,14 @@ private static function mapBorderStyle($cssBorderStyle)
582621
private static function mapAlign($cssAlignment)
583622
{
584623
switch ($cssAlignment) {
585-
case 'left':
586-
return Jc::START;
587624
case 'right':
588625
return Jc::END;
589626
case 'center':
590627
return Jc::CENTER;
591628
case 'justify':
592629
return Jc::BOTH;
630+
default:
631+
return Jc::START;
593632
}
594633

595634
return null;

0 commit comments

Comments
 (0)