PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Helper / Html.php

Html.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Helper/Html.php

872 lines 22.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TablePress\PhpOffice\PhpSpreadsheet\Helper;
4
5 use DOMAttr;
6 use DOMDocument;
7 use DOMElement;
8 use DOMNode;
9 use DOMText;
10 use TablePress\PhpOffice\PhpSpreadsheet\RichText\RichText;
11 use TablePress\PhpOffice\PhpSpreadsheet\Style\Color;
12 use TablePress\PhpOffice\PhpSpreadsheet\Style\Font;
13
14 class Html
15 {
16 protected const COLOUR_MAP = [
17 'aliceblue' => 'f0f8ff',
18 'antiquewhite' => 'faebd7',
19 'antiquewhite1' => 'ffefdb',
20 'antiquewhite2' => 'eedfcc',
21 'antiquewhite3' => 'cdc0b0',
22 'antiquewhite4' => '8b8378',
23 'aqua' => '00ffff',
24 'aquamarine1' => '7fffd4',
25 'aquamarine2' => '76eec6',
26 'aquamarine4' => '458b74',
27 'azure1' => 'f0ffff',
28 'azure2' => 'e0eeee',
29 'azure3' => 'c1cdcd',
30 'azure4' => '838b8b',
31 'beige' => 'f5f5dc',
32 'bisque1' => 'ffe4c4',
33 'bisque2' => 'eed5b7',
34 'bisque3' => 'cdb79e',
35 'bisque4' => '8b7d6b',
36 'black' => '000000',
37 'blanchedalmond' => 'ffebcd',
38 'blue' => '0000ff',
39 'blue1' => '0000ff',
40 'blue2' => '0000ee',
41 'blue4' => '00008b',
42 'blueviolet' => '8a2be2',
43 'brown' => 'a52a2a',
44 'brown1' => 'ff4040',
45 'brown2' => 'ee3b3b',
46 'brown3' => 'cd3333',
47 'brown4' => '8b2323',
48 'burlywood' => 'deb887',
49 'burlywood1' => 'ffd39b',
50 'burlywood2' => 'eec591',
51 'burlywood3' => 'cdaa7d',
52 'burlywood4' => '8b7355',
53 'cadetblue' => '5f9ea0',
54 'cadetblue1' => '98f5ff',
55 'cadetblue2' => '8ee5ee',
56 'cadetblue3' => '7ac5cd',
57 'cadetblue4' => '53868b',
58 'chartreuse1' => '7fff00',
59 'chartreuse2' => '76ee00',
60 'chartreuse3' => '66cd00',
61 'chartreuse4' => '458b00',
62 'chocolate' => 'd2691e',
63 'chocolate1' => 'ff7f24',
64 'chocolate2' => 'ee7621',
65 'chocolate3' => 'cd661d',
66 'coral' => 'ff7f50',
67 'coral1' => 'ff7256',
68 'coral2' => 'ee6a50',
69 'coral3' => 'cd5b45',
70 'coral4' => '8b3e2f',
71 'cornflowerblue' => '6495ed',
72 'cornsilk1' => 'fff8dc',
73 'cornsilk2' => 'eee8cd',
74 'cornsilk3' => 'cdc8b1',
75 'cornsilk4' => '8b8878',
76 'cyan1' => '00ffff',
77 'cyan2' => '00eeee',
78 'cyan3' => '00cdcd',
79 'cyan4' => '008b8b',
80 'darkgoldenrod' => 'b8860b',
81 'darkgoldenrod1' => 'ffb90f',
82 'darkgoldenrod2' => 'eead0e',
83 'darkgoldenrod3' => 'cd950c',
84 'darkgoldenrod4' => '8b6508',
85 'darkgreen' => '006400',
86 'darkkhaki' => 'bdb76b',
87 'darkolivegreen' => '556b2f',
88 'darkolivegreen1' => 'caff70',
89 'darkolivegreen2' => 'bcee68',
90 'darkolivegreen3' => 'a2cd5a',
91 'darkolivegreen4' => '6e8b3d',
92 'darkorange' => 'ff8c00',
93 'darkorange1' => 'ff7f00',
94 'darkorange2' => 'ee7600',
95 'darkorange3' => 'cd6600',
96 'darkorange4' => '8b4500',
97 'darkorchid' => '9932cc',
98 'darkorchid1' => 'bf3eff',
99 'darkorchid2' => 'b23aee',
100 'darkorchid3' => '9a32cd',
101 'darkorchid4' => '68228b',
102 'darksalmon' => 'e9967a',
103 'darkseagreen' => '8fbc8f',
104 'darkseagreen1' => 'c1ffc1',
105 'darkseagreen2' => 'b4eeb4',
106 'darkseagreen3' => '9bcd9b',
107 'darkseagreen4' => '698b69',
108 'darkslateblue' => '483d8b',
109 'darkslategray' => '2f4f4f',
110 'darkslategray1' => '97ffff',
111 'darkslategray2' => '8deeee',
112 'darkslategray3' => '79cdcd',
113 'darkslategray4' => '528b8b',
114 'darkturquoise' => '00ced1',
115 'darkviolet' => '9400d3',
116 'deeppink1' => 'ff1493',
117 'deeppink2' => 'ee1289',
118 'deeppink3' => 'cd1076',
119 'deeppink4' => '8b0a50',
120 'deepskyblue1' => '00bfff',
121 'deepskyblue2' => '00b2ee',
122 'deepskyblue3' => '009acd',
123 'deepskyblue4' => '00688b',
124 'dimgray' => '696969',
125 'dodgerblue1' => '1e90ff',
126 'dodgerblue2' => '1c86ee',
127 'dodgerblue3' => '1874cd',
128 'dodgerblue4' => '104e8b',
129 'firebrick' => 'b22222',
130 'firebrick1' => 'ff3030',
131 'firebrick2' => 'ee2c2c',
132 'firebrick3' => 'cd2626',
133 'firebrick4' => '8b1a1a',
134 'floralwhite' => 'fffaf0',
135 'forestgreen' => '228b22',
136 'fuchsia' => 'ff00ff',
137 'gainsboro' => 'dcdcdc',
138 'ghostwhite' => 'f8f8ff',
139 'gold1' => 'ffd700',
140 'gold2' => 'eec900',
141 'gold3' => 'cdad00',
142 'gold4' => '8b7500',
143 'goldenrod' => 'daa520',
144 'goldenrod1' => 'ffc125',
145 'goldenrod2' => 'eeb422',
146 'goldenrod3' => 'cd9b1d',
147 'goldenrod4' => '8b6914',
148 'gray' => 'bebebe',
149 'gray1' => '030303',
150 'gray10' => '1a1a1a',
151 'gray11' => '1c1c1c',
152 'gray12' => '1f1f1f',
153 'gray13' => '212121',
154 'gray14' => '242424',
155 'gray15' => '262626',
156 'gray16' => '292929',
157 'gray17' => '2b2b2b',
158 'gray18' => '2e2e2e',
159 'gray19' => '303030',
160 'gray2' => '050505',
161 'gray20' => '333333',
162 'gray21' => '363636',
163 'gray22' => '383838',
164 'gray23' => '3b3b3b',
165 'gray24' => '3d3d3d',
166 'gray25' => '404040',
167 'gray26' => '424242',
168 'gray27' => '454545',
169 'gray28' => '474747',
170 'gray29' => '4a4a4a',
171 'gray3' => '080808',
172 'gray30' => '4d4d4d',
173 'gray31' => '4f4f4f',
174 'gray32' => '525252',
175 'gray33' => '545454',
176 'gray34' => '575757',
177 'gray35' => '595959',
178 'gray36' => '5c5c5c',
179 'gray37' => '5e5e5e',
180 'gray38' => '616161',
181 'gray39' => '636363',
182 'gray4' => '0a0a0a',
183 'gray40' => '666666',
184 'gray41' => '696969',
185 'gray42' => '6b6b6b',
186 'gray43' => '6e6e6e',
187 'gray44' => '707070',
188 'gray45' => '737373',
189 'gray46' => '757575',
190 'gray47' => '787878',
191 'gray48' => '7a7a7a',
192 'gray49' => '7d7d7d',
193 'gray5' => '0d0d0d',
194 'gray50' => '7f7f7f',
195 'gray51' => '828282',
196 'gray52' => '858585',
197 'gray53' => '878787',
198 'gray54' => '8a8a8a',
199 'gray55' => '8c8c8c',
200 'gray56' => '8f8f8f',
201 'gray57' => '919191',
202 'gray58' => '949494',
203 'gray59' => '969696',
204 'gray6' => '0f0f0f',
205 'gray60' => '999999',
206 'gray61' => '9c9c9c',
207 'gray62' => '9e9e9e',
208 'gray63' => 'a1a1a1',
209 'gray64' => 'a3a3a3',
210 'gray65' => 'a6a6a6',
211 'gray66' => 'a8a8a8',
212 'gray67' => 'ababab',
213 'gray68' => 'adadad',
214 'gray69' => 'b0b0b0',
215 'gray7' => '121212',
216 'gray70' => 'b3b3b3',
217 'gray71' => 'b5b5b5',
218 'gray72' => 'b8b8b8',
219 'gray73' => 'bababa',
220 'gray74' => 'bdbdbd',
221 'gray75' => 'bfbfbf',
222 'gray76' => 'c2c2c2',
223 'gray77' => 'c4c4c4',
224 'gray78' => 'c7c7c7',
225 'gray79' => 'c9c9c9',
226 'gray8' => '141414',
227 'gray80' => 'cccccc',
228 'gray81' => 'cfcfcf',
229 'gray82' => 'd1d1d1',
230 'gray83' => 'd4d4d4',
231 'gray84' => 'd6d6d6',
232 'gray85' => 'd9d9d9',
233 'gray86' => 'dbdbdb',
234 'gray87' => 'dedede',
235 'gray88' => 'e0e0e0',
236 'gray89' => 'e3e3e3',
237 'gray9' => '171717',
238 'gray90' => 'e5e5e5',
239 'gray91' => 'e8e8e8',
240 'gray92' => 'ebebeb',
241 'gray93' => 'ededed',
242 'gray94' => 'f0f0f0',
243 'gray95' => 'f2f2f2',
244 'gray97' => 'f7f7f7',
245 'gray98' => 'fafafa',
246 'gray99' => 'fcfcfc',
247 'green' => '00ff00',
248 'green1' => '00ff00',
249 'green2' => '00ee00',
250 'green3' => '00cd00',
251 'green4' => '008b00',
252 'greenyellow' => 'adff2f',
253 'honeydew1' => 'f0fff0',
254 'honeydew2' => 'e0eee0',
255 'honeydew3' => 'c1cdc1',
256 'honeydew4' => '838b83',
257 'hotpink' => 'ff69b4',
258 'hotpink1' => 'ff6eb4',
259 'hotpink2' => 'ee6aa7',
260 'hotpink3' => 'cd6090',
261 'hotpink4' => '8b3a62',
262 'indianred' => 'cd5c5c',
263 'indianred1' => 'ff6a6a',
264 'indianred2' => 'ee6363',
265 'indianred3' => 'cd5555',
266 'indianred4' => '8b3a3a',
267 'ivory1' => 'fffff0',
268 'ivory2' => 'eeeee0',
269 'ivory3' => 'cdcdc1',
270 'ivory4' => '8b8b83',
271 'khaki' => 'f0e68c',
272 'khaki1' => 'fff68f',
273 'khaki2' => 'eee685',
274 'khaki3' => 'cdc673',
275 'khaki4' => '8b864e',
276 'lavender' => 'e6e6fa',
277 'lavenderblush1' => 'fff0f5',
278 'lavenderblush2' => 'eee0e5',
279 'lavenderblush3' => 'cdc1c5',
280 'lavenderblush4' => '8b8386',
281 'lawngreen' => '7cfc00',
282 'lemonchiffon1' => 'fffacd',
283 'lemonchiffon2' => 'eee9bf',
284 'lemonchiffon3' => 'cdc9a5',
285 'lemonchiffon4' => '8b8970',
286 'light' => 'eedd82',
287 'lightblue' => 'add8e6',
288 'lightblue1' => 'bfefff',
289 'lightblue2' => 'b2dfee',
290 'lightblue3' => '9ac0cd',
291 'lightblue4' => '68838b',
292 'lightcoral' => 'f08080',
293 'lightcyan1' => 'e0ffff',
294 'lightcyan2' => 'd1eeee',
295 'lightcyan3' => 'b4cdcd',
296 'lightcyan4' => '7a8b8b',
297 'lightgoldenrod1' => 'ffec8b',
298 'lightgoldenrod2' => 'eedc82',
299 'lightgoldenrod3' => 'cdbe70',
300 'lightgoldenrod4' => '8b814c',
301 'lightgoldenrodyellow' => 'fafad2',
302 'lightgray' => 'd3d3d3',
303 'lightpink' => 'ffb6c1',
304 'lightpink1' => 'ffaeb9',
305 'lightpink2' => 'eea2ad',
306 'lightpink3' => 'cd8c95',
307 'lightpink4' => '8b5f65',
308 'lightsalmon1' => 'ffa07a',
309 'lightsalmon2' => 'ee9572',
310 'lightsalmon3' => 'cd8162',
311 'lightsalmon4' => '8b5742',
312 'lightseagreen' => '20b2aa',
313 'lightskyblue' => '87cefa',
314 'lightskyblue1' => 'b0e2ff',
315 'lightskyblue2' => 'a4d3ee',
316 'lightskyblue3' => '8db6cd',
317 'lightskyblue4' => '607b8b',
318 'lightslateblue' => '8470ff',
319 'lightslategray' => '778899',
320 'lightsteelblue' => 'b0c4de',
321 'lightsteelblue1' => 'cae1ff',
322 'lightsteelblue2' => 'bcd2ee',
323 'lightsteelblue3' => 'a2b5cd',
324 'lightsteelblue4' => '6e7b8b',
325 'lightyellow1' => 'ffffe0',
326 'lightyellow2' => 'eeeed1',
327 'lightyellow3' => 'cdcdb4',
328 'lightyellow4' => '8b8b7a',
329 'lime' => '00ff00',
330 'limegreen' => '32cd32',
331 'linen' => 'faf0e6',
332 'magenta' => 'ff00ff',
333 'magenta2' => 'ee00ee',
334 'magenta3' => 'cd00cd',
335 'magenta4' => '8b008b',
336 'maroon' => 'b03060',
337 'maroon1' => 'ff34b3',
338 'maroon2' => 'ee30a7',
339 'maroon3' => 'cd2990',
340 'maroon4' => '8b1c62',
341 'medium' => '66cdaa',
342 'mediumaquamarine' => '66cdaa',
343 'mediumblue' => '0000cd',
344 'mediumorchid' => 'ba55d3',
345 'mediumorchid1' => 'e066ff',
346 'mediumorchid2' => 'd15fee',
347 'mediumorchid3' => 'b452cd',
348 'mediumorchid4' => '7a378b',
349 'mediumpurple' => '9370db',
350 'mediumpurple1' => 'ab82ff',
351 'mediumpurple2' => '9f79ee',
352 'mediumpurple3' => '8968cd',
353 'mediumpurple4' => '5d478b',
354 'mediumseagreen' => '3cb371',
355 'mediumslateblue' => '7b68ee',
356 'mediumspringgreen' => '00fa9a',
357 'mediumturquoise' => '48d1cc',
358 'mediumvioletred' => 'c71585',
359 'midnightblue' => '191970',
360 'mintcream' => 'f5fffa',
361 'mistyrose1' => 'ffe4e1',
362 'mistyrose2' => 'eed5d2',
363 'mistyrose3' => 'cdb7b5',
364 'mistyrose4' => '8b7d7b',
365 'moccasin' => 'ffe4b5',
366 'navajowhite1' => 'ffdead',
367 'navajowhite2' => 'eecfa1',
368 'navajowhite3' => 'cdb38b',
369 'navajowhite4' => '8b795e',
370 'navy' => '000080',
371 'navyblue' => '000080',
372 'oldlace' => 'fdf5e6',
373 'olive' => '808000',
374 'olivedrab' => '6b8e23',
375 'olivedrab1' => 'c0ff3e',
376 'olivedrab2' => 'b3ee3a',
377 'olivedrab4' => '698b22',
378 'orange' => 'ffa500',
379 'orange1' => 'ffa500',
380 'orange2' => 'ee9a00',
381 'orange3' => 'cd8500',
382 'orange4' => '8b5a00',
383 'orangered1' => 'ff4500',
384 'orangered2' => 'ee4000',
385 'orangered3' => 'cd3700',
386 'orangered4' => '8b2500',
387 'orchid' => 'da70d6',
388 'orchid1' => 'ff83fa',
389 'orchid2' => 'ee7ae9',
390 'orchid3' => 'cd69c9',
391 'orchid4' => '8b4789',
392 'pale' => 'db7093',
393 'palegoldenrod' => 'eee8aa',
394 'palegreen' => '98fb98',
395 'palegreen1' => '9aff9a',
396 'palegreen2' => '90ee90',
397 'palegreen3' => '7ccd7c',
398 'palegreen4' => '548b54',
399 'paleturquoise' => 'afeeee',
400 'paleturquoise1' => 'bbffff',
401 'paleturquoise2' => 'aeeeee',
402 'paleturquoise3' => '96cdcd',
403 'paleturquoise4' => '668b8b',
404 'palevioletred' => 'db7093',
405 'palevioletred1' => 'ff82ab',
406 'palevioletred2' => 'ee799f',
407 'palevioletred3' => 'cd6889',
408 'palevioletred4' => '8b475d',
409 'papayawhip' => 'ffefd5',
410 'peachpuff1' => 'ffdab9',
411 'peachpuff2' => 'eecbad',
412 'peachpuff3' => 'cdaf95',
413 'peachpuff4' => '8b7765',
414 'pink' => 'ffc0cb',
415 'pink1' => 'ffb5c5',
416 'pink2' => 'eea9b8',
417 'pink3' => 'cd919e',
418 'pink4' => '8b636c',
419 'plum' => 'dda0dd',
420 'plum1' => 'ffbbff',
421 'plum2' => 'eeaeee',
422 'plum3' => 'cd96cd',
423 'plum4' => '8b668b',
424 'powderblue' => 'b0e0e6',
425 'purple' => 'a020f0',
426 'rebeccapurple' => '663399',
427 'purple1' => '9b30ff',
428 'purple2' => '912cee',
429 'purple3' => '7d26cd',
430 'purple4' => '551a8b',
431 'red' => 'ff0000',
432 'red1' => 'ff0000',
433 'red2' => 'ee0000',
434 'red3' => 'cd0000',
435 'red4' => '8b0000',
436 'rosybrown' => 'bc8f8f',
437 'rosybrown1' => 'ffc1c1',
438 'rosybrown2' => 'eeb4b4',
439 'rosybrown3' => 'cd9b9b',
440 'rosybrown4' => '8b6969',
441 'royalblue' => '4169e1',
442 'royalblue1' => '4876ff',
443 'royalblue2' => '436eee',
444 'royalblue3' => '3a5fcd',
445 'royalblue4' => '27408b',
446 'saddlebrown' => '8b4513',
447 'salmon' => 'fa8072',
448 'salmon1' => 'ff8c69',
449 'salmon2' => 'ee8262',
450 'salmon3' => 'cd7054',
451 'salmon4' => '8b4c39',
452 'sandybrown' => 'f4a460',
453 'seagreen1' => '54ff9f',
454 'seagreen2' => '4eee94',
455 'seagreen3' => '43cd80',
456 'seagreen4' => '2e8b57',
457 'seashell1' => 'fff5ee',
458 'seashell2' => 'eee5de',
459 'seashell3' => 'cdc5bf',
460 'seashell4' => '8b8682',
461 'sienna' => 'a0522d',
462 'sienna1' => 'ff8247',
463 'sienna2' => 'ee7942',
464 'sienna3' => 'cd6839',
465 'sienna4' => '8b4726',
466 'silver' => 'c0c0c0',
467 'skyblue' => '87ceeb',
468 'skyblue1' => '87ceff',
469 'skyblue2' => '7ec0ee',
470 'skyblue3' => '6ca6cd',
471 'skyblue4' => '4a708b',
472 'slateblue' => '6a5acd',
473 'slateblue1' => '836fff',
474 'slateblue2' => '7a67ee',
475 'slateblue3' => '6959cd',
476 'slateblue4' => '473c8b',
477 'slategray' => '708090',
478 'slategray1' => 'c6e2ff',
479 'slategray2' => 'b9d3ee',
480 'slategray3' => '9fb6cd',
481 'slategray4' => '6c7b8b',
482 'snow1' => 'fffafa',
483 'snow2' => 'eee9e9',
484 'snow3' => 'cdc9c9',
485 'snow4' => '8b8989',
486 'springgreen1' => '00ff7f',
487 'springgreen2' => '00ee76',
488 'springgreen3' => '00cd66',
489 'springgreen4' => '008b45',
490 'steelblue' => '4682b4',
491 'steelblue1' => '63b8ff',
492 'steelblue2' => '5cacee',
493 'steelblue3' => '4f94cd',
494 'steelblue4' => '36648b',
495 'tan' => 'd2b48c',
496 'tan1' => 'ffa54f',
497 'tan2' => 'ee9a49',
498 'tan3' => 'cd853f',
499 'tan4' => '8b5a2b',
500 'teal' => '008080',
501 'thistle' => 'd8bfd8',
502 'thistle1' => 'ffe1ff',
503 'thistle2' => 'eed2ee',
504 'thistle3' => 'cdb5cd',
505 'thistle4' => '8b7b8b',
506 'tomato1' => 'ff6347',
507 'tomato2' => 'ee5c42',
508 'tomato3' => 'cd4f39',
509 'tomato4' => '8b3626',
510 'turquoise' => '40e0d0',
511 'turquoise1' => '00f5ff',
512 'turquoise2' => '00e5ee',
513 'turquoise3' => '00c5cd',
514 'turquoise4' => '00868b',
515 'violet' => 'ee82ee',
516 'violetred' => 'd02090',
517 'violetred1' => 'ff3e96',
518 'violetred2' => 'ee3a8c',
519 'violetred3' => 'cd3278',
520 'violetred4' => '8b2252',
521 'wheat' => 'f5deb3',
522 'wheat1' => 'ffe7ba',
523 'wheat2' => 'eed8ae',
524 'wheat3' => 'cdba96',
525 'wheat4' => '8b7e66',
526 'white' => 'ffffff',
527 'whitesmoke' => 'f5f5f5',
528 'yellow' => 'ffff00',
529 'yellow1' => 'ffff00',
530 'yellow2' => 'eeee00',
531 'yellow3' => 'cdcd00',
532 'yellow4' => '8b8b00',
533 'yellowgreen' => '9acd32',
534 ];
535
536 private ?string $face = null;
537
538 private ?string $size = null;
539
540 private ?string $color = null;
541
542 private bool $bold = false;
543
544 private bool $italic = false;
545
546 private bool $underline = false;
547
548 private bool $superscript = false;
549
550 private bool $subscript = false;
551
552 private bool $strikethrough = false;
553
554 /** @var callable[] */
555 protected array $startTagCallbacks;
556
557 /** @var callable[] */
558 protected array $endTagCallbacks;
559
560 /** @var mixed[] */
561 private array $stack = [];
562
563 public string $stringData = '';
564
565 private RichText $richTextObject;
566
567 private bool $preserveWhiteSpace = false;
568
569 public function __construct()
570 {
571 if (!isset($this->startTagCallbacks)) {
572 $this->startTagCallbacks = [
573 'font' => \Closure::fromCallable([$this, 'startFontTag']),
574 'b' => \Closure::fromCallable([$this, 'startBoldTag']),
575 'strong' => \Closure::fromCallable([$this, 'startBoldTag']),
576 'i' => \Closure::fromCallable([$this, 'startItalicTag']),
577 'em' => \Closure::fromCallable([$this, 'startItalicTag']),
578 'u' => \Closure::fromCallable([$this, 'startUnderlineTag']),
579 'ins' => \Closure::fromCallable([$this, 'startUnderlineTag']),
580 'del' => \Closure::fromCallable([$this, 'startStrikethruTag']),
581 's' => \Closure::fromCallable([$this, 'startStrikethruTag']),
582 'sup' => \Closure::fromCallable([$this, 'startSuperscriptTag']),
583 'sub' => \Closure::fromCallable([$this, 'startSubscriptTag']),
584 ];
585 }
586 if (!isset($this->endTagCallbacks)) {
587 $this->endTagCallbacks = [
588 'font' => \Closure::fromCallable([$this, 'endFontTag']),
589 'b' => \Closure::fromCallable([$this, 'endBoldTag']),
590 'strong' => \Closure::fromCallable([$this, 'endBoldTag']),
591 'i' => \Closure::fromCallable([$this, 'endItalicTag']),
592 'em' => \Closure::fromCallable([$this, 'endItalicTag']),
593 'u' => \Closure::fromCallable([$this, 'endUnderlineTag']),
594 'ins' => \Closure::fromCallable([$this, 'endUnderlineTag']),
595 'del' => \Closure::fromCallable([$this, 'endStrikethruTag']),
596 's' => \Closure::fromCallable([$this, 'endStrikethruTag']),
597 'sup' => \Closure::fromCallable([$this, 'endSuperscriptTag']),
598 'sub' => \Closure::fromCallable([$this, 'endSubscriptTag']),
599 'br' => \Closure::fromCallable([$this, 'breakTag']),
600 'p' => \Closure::fromCallable([$this, 'breakTag']),
601 'h1' => \Closure::fromCallable([$this, 'breakTag']),
602 'h2' => \Closure::fromCallable([$this, 'breakTag']),
603 'h3' => \Closure::fromCallable([$this, 'breakTag']),
604 'h4' => \Closure::fromCallable([$this, 'breakTag']),
605 'h5' => \Closure::fromCallable([$this, 'breakTag']),
606 'h6' => \Closure::fromCallable([$this, 'breakTag']),
607 ];
608 }
609 }
610
611 private function initialise(): void
612 {
613 $this->face = $this->size = $this->color = null;
614 $this->bold = $this->italic = $this->underline = $this->superscript = $this->subscript = $this->strikethrough = false;
615
616 $this->stack = [];
617
618 $this->stringData = '';
619 }
620
621 /**
622 * Parse HTML formatting and return the resulting RichText.
623 */
624 public function toRichTextObject(string $html, bool $preserveWhiteSpace = false): RichText
625 {
626 $this->initialise();
627
628 // Create a new DOM object
629 $dom = new DOMDocument();
630 // Load the HTML file into the DOM object
631 // Note the use of error suppression, because typically this will be an html fragment, so not fully valid markup
632 $prefix = '<?xml encoding="UTF-8">';
633 @$dom->loadHTML($prefix . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
634 // Discard excess white space
635 $dom->preserveWhiteSpace = false;
636
637 $this->richTextObject = new RichText();
638 $this->preserveWhiteSpace = $preserveWhiteSpace;
639 $this->parseElements($dom);
640 $this->preserveWhiteSpace = false;
641
642 // Clean any further spurious whitespace
643 $this->cleanWhitespace();
644
645 return $this->richTextObject;
646 }
647
648 private function cleanWhitespace(): void
649 {
650 foreach ($this->richTextObject->getRichTextElements() as $key => $element) {
651 $text = $element->getText();
652 // Trim any leading spaces on the first run
653 if ($key == 0) {
654 $text = ltrim($text);
655 }
656 // Trim any spaces immediately after a line break
657 $text = (string) preg_replace('/\n */mu', "\n", $text);
658 $element->setText($text);
659 }
660 }
661
662 private function buildTextRun(): void
663 {
664 $text = $this->stringData;
665 if (trim($text) === '') {
666 return;
667 }
668
669 $richtextRun = $this->richTextObject->createTextRun($this->stringData);
670 $font = $richtextRun->getFont();
671 if ($font !== null) {
672 if ($this->face) {
673 $font->setName($this->face);
674 }
675 if ($this->size) {
676 $font->setSize($this->size);
677 }
678 if ($this->color) {
679 $font->setColor(new Color('ff' . $this->color));
680 }
681 if ($this->bold) {
682 $font->setBold(true);
683 }
684 if ($this->italic) {
685 $font->setItalic(true);
686 }
687 if ($this->underline) {
688 $font->setUnderline(Font::UNDERLINE_SINGLE);
689 }
690 if ($this->superscript) {
691 $font->setSuperscript(true);
692 }
693 if ($this->subscript) {
694 $font->setSubscript(true);
695 }
696 if ($this->strikethrough) {
697 $font->setStrikethrough(true);
698 }
699 }
700 $this->stringData = '';
701 }
702
703 private function rgbToColour(string $rgbValue): string
704 {
705 preg_match_all('/\d+/', $rgbValue, $values);
706 foreach ($values[0] as &$value) {
707 $value = str_pad(dechex((int) $value), 2, '0', STR_PAD_LEFT);
708 }
709
710 return implode('', $values[0]);
711 }
712
713 public static function colourNameLookup(string $colorName): string
714 {
715 /** @var string[] */
716 $temp = static::COLOUR_MAP;
717
718 return $temp[$colorName] ?? '';
719 }
720
721 protected function startFontTag(DOMElement $tag): void
722 {
723 $attrs = $tag->attributes ?? [];
724 /** @var DOMAttr $attribute */
725 foreach ($attrs as $attribute) {
726 $attributeName = strtolower($attribute->name);
727 $attributeName = preg_replace('/^html:/', '', $attributeName) ?? $attributeName; // in case from Xml spreadsheet
728 $attributeValue = $attribute->value;
729
730 if ($attributeName === 'color') {
731 if (preg_match('/rgb\s*\(/', $attributeValue)) {
732 $this->$attributeName = $this->rgbToColour($attributeValue);
733 } elseif (str_starts_with(trim($attributeValue), '#')) {
734 $this->$attributeName = ltrim($attributeValue, '#');
735 } else {
736 $this->$attributeName = static::colourNameLookup($attributeValue);
737 }
738 } elseif ($attributeName === 'face' || $attributeName === 'size') {
739 $this->$attributeName = $attributeValue;
740 }
741 }
742 }
743
744 protected function endFontTag(): void
745 {
746 $this->face = $this->size = $this->color = null;
747 }
748
749 protected function startBoldTag(): void
750 {
751 $this->bold = true;
752 }
753
754 protected function endBoldTag(): void
755 {
756 $this->bold = false;
757 }
758
759 protected function startItalicTag(): void
760 {
761 $this->italic = true;
762 }
763
764 protected function endItalicTag(): void
765 {
766 $this->italic = false;
767 }
768
769 protected function startUnderlineTag(): void
770 {
771 $this->underline = true;
772 }
773
774 protected function endUnderlineTag(): void
775 {
776 $this->underline = false;
777 }
778
779 protected function startSubscriptTag(): void
780 {
781 $this->subscript = true;
782 }
783
784 protected function endSubscriptTag(): void
785 {
786 $this->subscript = false;
787 }
788
789 protected function startSuperscriptTag(): void
790 {
791 $this->superscript = true;
792 }
793
794 protected function endSuperscriptTag(): void
795 {
796 $this->superscript = false;
797 }
798
799 protected function startStrikethruTag(): void
800 {
801 $this->strikethrough = true;
802 }
803
804 protected function endStrikethruTag(): void
805 {
806 $this->strikethrough = false;
807 }
808
809 public function breakTag(): void
810 {
811 $this->stringData .= "\n";
812 }
813
814 private function parseTextNode(DOMText $textNode): void
815 {
816 if ($this->preserveWhiteSpace) {
817 $domText = $textNode->nodeValue ?? '';
818 } else {
819 $domText = (string) preg_replace(
820 '/\s+/u',
821 ' ',
822 str_replace(["\r", "\n"], ' ', $textNode->nodeValue ?? '')
823 );
824 }
825 $this->stringData .= $domText;
826 $this->buildTextRun();
827 }
828
829 public function addStartTagCallback(string $tag, callable $callback): void
830 {
831 $this->startTagCallbacks[$tag] = $callback;
832 }
833
834 public function addEndTagCallback(string $tag, callable $callback): void
835 {
836 $this->endTagCallbacks[$tag] = $callback;
837 }
838
839 /** @param callable[] $callbacks */
840 private function handleCallback(DOMElement $element, string $callbackTag, array $callbacks): void
841 {
842 if (isset($callbacks[$callbackTag])) {
843 $elementHandler = $callbacks[$callbackTag];
844 call_user_func($elementHandler, $element, $this);
845 }
846 }
847
848 private function parseElementNode(DOMElement $element): void
849 {
850 $callbackTag = strtolower($element->nodeName);
851 $this->stack[] = $callbackTag;
852
853 $this->handleCallback($element, $callbackTag, $this->startTagCallbacks);
854
855 $this->parseElements($element);
856 array_pop($this->stack);
857
858 $this->handleCallback($element, $callbackTag, $this->endTagCallbacks);
859 }
860
861 private function parseElements(DOMNode $element): void
862 {
863 foreach ($element->childNodes ?? [] as $child) {
864 if ($child instanceof DOMText) {
865 $this->parseTextNode($child);
866 } elseif ($child instanceof DOMElement) {
867 $this->parseElementNode($child);
868 }
869 }
870 }
871 }
872