18
18
namespace PhpOffice \PhpWord \Shared ;
19
19
20
20
use PhpOffice \PhpWord \Element \AbstractContainer ;
21
+ use PhpOffice \PhpWord \Element \Row ;
22
+ use PhpOffice \PhpWord \Element \Table ;
21
23
use PhpOffice \PhpWord \SimpleType \Jc ;
22
24
23
25
/**
@@ -99,7 +101,7 @@ protected static function parseInlineStyle($node, $styles = array())
99
101
protected static function parseNode ($ node , $ element , $ styles = array (), $ data = array ())
100
102
{
101
103
// Populate styles array
102
- $ styleTypes = array ('font ' , 'paragraph ' , 'list ' );
104
+ $ styleTypes = array ('font ' , 'paragraph ' , 'list ' , ' table ' , ' row ' , ' cell ' );
103
105
foreach ($ styleTypes as $ styleType ) {
104
106
if (!isset ($ styles [$ styleType ])) {
105
107
$ styles [$ styleType ] = array ();
@@ -124,10 +126,11 @@ protected static function parseNode($node, $element, $styles = array(), $data =
124
126
'u ' => array ('Property ' , null , null , $ styles , null , 'underline ' , 'single ' ),
125
127
'sup ' => array ('Property ' , null , null , $ styles , null , 'superScript ' , true ),
126
128
'sub ' => array ('Property ' , null , null , $ styles , null , 'subScript ' , true ),
127
- 'span ' => array ('Property ' , null , null , $ styles , null , 'span ' , $ node ),
128
- 'table ' => array ('Table ' , $ node , $ element , $ styles , null , 'addTable ' , true ),
129
- 'tr ' => array ('Table ' , $ node , $ element , $ styles , null , 'addRow ' , true ),
130
- 'td ' => array ('Table ' , $ node , $ element , $ styles , null , 'addCell ' , true ),
129
+ 'span ' => array ('Span ' , $ node , null , $ styles , null , null , null ),
130
+ 'table ' => array ('Table ' , $ node , $ element , $ styles , null , null , null ),
131
+ 'tr ' => array ('Row ' , $ node , $ element , $ styles , null , null , null ),
132
+ 'td ' => array ('Cell ' , $ node , $ element , $ styles , null , null , null ),
133
+ 'th ' => array ('Cell ' , $ node , $ element , $ styles , null , null , null ),
131
134
'ul ' => array ('List ' , null , null , $ styles , $ data , 3 , null ),
132
135
'ol ' => array ('List ' , null , null , $ styles , $ data , 7 , null ),
133
136
'li ' => array ('ListItem ' , $ node , $ element , $ styles , $ data , null , null ),
@@ -179,7 +182,7 @@ private static function parseChildNodes($node, $element, $styles, $data)
179
182
$ cNodes = $ node ->childNodes ;
180
183
if (count ($ cNodes ) > 0 ) {
181
184
foreach ($ cNodes as $ cNode ) {
182
- if ($ element instanceof AbstractContainer) {
185
+ if ($ element instanceof AbstractContainer || $ element instanceof Table || $ element instanceof Row ) {
183
186
self ::parseNode ($ cNode , $ element , $ styles , $ data );
184
187
}
185
188
}
@@ -197,7 +200,7 @@ private static function parseChildNodes($node, $element, $styles, $data)
197
200
*/
198
201
private static function parseParagraph ($ node , $ element , &$ styles )
199
202
{
200
- $ styles ['paragraph ' ] = self ::parseInlineStyle ($ node , $ styles ['paragraph ' ]);
203
+ $ styles ['paragraph ' ] = self ::recursiveParseStylesInHierarchy ($ node , $ styles ['paragraph ' ]);
201
204
$ newElement = $ element ->addTextRun ($ styles ['paragraph ' ]);
202
205
203
206
return $ newElement ;
@@ -231,7 +234,12 @@ private static function parseHeading($element, &$styles, $argument1)
231
234
*/
232
235
private static function parseText ($ node , $ element , &$ styles )
233
236
{
234
- $ styles ['font ' ] = self ::parseInlineStyle ($ node , $ styles ['font ' ]);
237
+ $ styles ['font ' ] = self ::recursiveParseStylesInHierarchy ($ node , $ styles ['font ' ]);
238
+
239
+ //alignment applies on paragraph, not on font. Let's copy it there
240
+ if (isset ($ styles ['font ' ]['alignment ' ])) {
241
+ $ styles ['paragraph ' ]['alignment ' ] = $ styles ['font ' ]['alignment ' ];
242
+ }
235
243
236
244
if (is_callable (array ($ element , 'addText ' ))) {
237
245
$ element ->addText ($ node ->nodeValue , $ styles ['font ' ], $ styles ['paragraph ' ]);
@@ -247,16 +255,18 @@ private static function parseText($node, $element, &$styles)
247
255
*/
248
256
private static function parseProperty (&$ styles , $ argument1 , $ argument2 )
249
257
{
250
- if ($ argument1 !== 'span ' ) {
251
- $ styles ['font ' ][$ argument1 ] = $ argument2 ;
252
- } else {
253
- if (!is_null ($ argument2 ->attributes )) {
254
- $ nodeAttr = $ argument2 ->attributes ->getNamedItem ('style ' );
255
- if (!is_null ($ nodeAttr ) && property_exists ($ nodeAttr , 'value ' )) {
256
- $ styles ['font ' ] = self ::parseStyle ($ nodeAttr , $ styles ['font ' ]);
257
- }
258
- }
259
- }
258
+ $ styles ['font ' ][$ argument1 ] = $ argument2 ;
259
+ }
260
+
261
+ /**
262
+ * Parse span node
263
+ *
264
+ * @param \DOMNode $node
265
+ * @param array &$styles
266
+ */
267
+ private static function parseSpan ($ node , &$ styles )
268
+ {
269
+ self ::parseInlineStyle ($ node , $ styles ['font ' ]);
260
270
}
261
271
262
272
/**
@@ -270,11 +280,11 @@ private static function parseProperty(&$styles, $argument1, $argument2)
270
280
*
271
281
* @todo As soon as TableItem, RowItem and CellItem support relative width and height
272
282
*/
273
- private static function parseTable ($ node , $ element , &$ styles, $ argument1 )
283
+ private static function parseTable ($ node , $ element , &$ styles )
274
284
{
275
- $ styles [ ' paragraph ' ] = self ::parseInlineStyle ($ node , $ styles ['paragraph ' ]);
285
+ $ elementStyles = self ::parseInlineStyle ($ node , $ styles ['table ' ]);
276
286
277
- $ newElement = $ element ->$ argument1 ( );
287
+ $ newElement = $ element ->addTable ( $ elementStyles );
278
288
279
289
// $attributes = $node->attributes;
280
290
// if ($attributes->getNamedItem('width') !== null) {
@@ -291,6 +301,62 @@ private static function parseTable($node, $element, &$styles, $argument1)
291
301
return $ newElement ;
292
302
}
293
303
304
+ /**
305
+ * Parse a table row
306
+ *
307
+ * @param \DOMNode $node
308
+ * @param \PhpOffice\PhpWord\Element\Table $element
309
+ * @param array &$styles
310
+ * @return \PhpOffice\PhpWord\Element\AbstractContainer $element
311
+ */
312
+ private static function parseRow ($ node , $ element , &$ styles )
313
+ {
314
+ $ rowStyles = self ::parseInlineStyle ($ node , $ styles ['row ' ]);
315
+ if ($ node ->parentNode ->nodeName == 'thead ' ) {
316
+ $ rowStyles ['tblHeader ' ] = true ;
317
+ }
318
+
319
+ return $ element ->addRow (null , $ rowStyles );
320
+ }
321
+
322
+ /**
323
+ * Parse table cell
324
+ *
325
+ * @param \DOMNode $node
326
+ * @param \PhpOffice\PhpWord\Element\Table $element
327
+ * @param array &$styles
328
+ * @return \PhpOffice\PhpWord\Element\AbstractContainer $element
329
+ */
330
+ private static function parseCell ($ node , $ element , &$ styles )
331
+ {
332
+ $ cellStyles = self ::recursiveParseStylesInHierarchy ($ node , $ styles ['cell ' ]);
333
+
334
+ $ colspan = $ node ->getAttribute ('colspan ' );
335
+ if (!empty ($ colspan )) {
336
+ $ cellStyles ['gridSpan ' ] = $ colspan - 0 ;
337
+ }
338
+
339
+ return $ element ->addCell (null , $ cellStyles );
340
+ }
341
+
342
+ /**
343
+ * Recursively parses styles on parent nodes
344
+ * TODO if too slow, add caching of parent nodes, !! everything is static here so watch out for concurrency !!
345
+ *
346
+ * @param \DOMNode $node
347
+ * @param array &$styles
348
+ */
349
+ private static function recursiveParseStylesInHierarchy (\DOMNode $ node , array $ style )
350
+ {
351
+ $ parentStyle = self ::parseInlineStyle ($ node , array ());
352
+ $ style = array_merge ($ parentStyle , $ style );
353
+ if ($ node ->parentNode != null && XML_ELEMENT_NODE == $ node ->parentNode ->nodeType ) {
354
+ $ style = self ::recursiveParseStylesInHierarchy ($ node ->parentNode , $ style );
355
+ }
356
+
357
+ return $ style ;
358
+ }
359
+
294
360
/**
295
361
* Parse list node
296
362
*
@@ -400,9 +466,59 @@ private static function parseStyle($attribute, $styles)
400
466
}
401
467
$ styles ['italic ' ] = $ tValue ;
402
468
break ;
469
+ case 'border-color ' :
470
+ $ styles ['color ' ] = trim ($ cValue , '# ' );
471
+ break ;
472
+ case 'border-width ' :
473
+ $ styles ['borderSize ' ] = Converter::cssToPoint ($ cValue );
474
+ break ;
475
+ case 'border-style ' :
476
+ $ styles ['borderStyle ' ] = self ::mapBorderStyle ($ cValue );
477
+ break ;
478
+ case 'width ' :
479
+ if (preg_match ('/([0-9]+[a-z]+)/ ' , $ cValue , $ matches )) {
480
+ $ styles ['width ' ] = Converter::cssToTwip ($ matches [1 ]);
481
+ $ styles ['unit ' ] = \PhpOffice \PhpWord \Style \Table::WIDTH_TWIP ;
482
+ } elseif (preg_match ('/([0-9]+)%/ ' , $ cValue , $ matches )) {
483
+ $ styles ['width ' ] = $ matches [1 ] * 50 ;
484
+ $ styles ['unit ' ] = \PhpOffice \PhpWord \Style \Table::WIDTH_PERCENT ;
485
+ } elseif (preg_match ('/([0-9]+)/ ' , $ cValue , $ matches )) {
486
+ $ styles ['width ' ] = $ matches [1 ];
487
+ $ styles ['unit ' ] = \PhpOffice \PhpWord \Style \Table::WIDTH_AUTO ;
488
+ }
489
+ break ;
490
+ case 'border ' :
491
+ if (preg_match ('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+)\s+([a-z]+)/ ' , $ cValue , $ matches )) {
492
+ $ styles ['borderSize ' ] = Converter::cssToPoint ($ matches [1 ]);
493
+ $ styles ['borderColor ' ] = trim ($ matches [2 ], '# ' );
494
+ $ styles ['borderStyle ' ] = self ::mapBorderStyle ($ matches [3 ]);
495
+ }
496
+ break ;
403
497
}
404
498
}
405
499
406
500
return $ styles ;
407
501
}
502
+
503
+ /**
504
+ * Transforms a CSS border style into a word border style
505
+ *
506
+ * @param string $cssBorderStyle
507
+ * @return null|string
508
+ */
509
+ private static function mapBorderStyle ($ cssBorderStyle )
510
+ {
511
+ if ($ cssBorderStyle == null ) {
512
+ return null ;
513
+ }
514
+ switch ($ cssBorderStyle ) {
515
+ case 'none ' :
516
+ case 'dashed ' :
517
+ case 'dotted ' :
518
+ case 'double ' :
519
+ return $ cssBorderStyle ;
520
+ case 'solid ' :
521
+ return 'single ' ;
522
+ }
523
+ }
408
524
}
0 commit comments