1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace League\HTMLToMarkdown\Converter; |
||
6 | |||
7 | use League\HTMLToMarkdown\Coerce; |
||
8 | use League\HTMLToMarkdown\Configuration; |
||
9 | use League\HTMLToMarkdown\ConfigurationAwareInterface; |
||
10 | use League\HTMLToMarkdown\ElementInterface; |
||
11 | use League\HTMLToMarkdown\PreConverterInterface; |
||
12 | |||
13 | class TableConverter implements ConverterInterface, PreConverterInterface, ConfigurationAwareInterface |
||
14 | { |
||
15 | /** @var Configuration */ |
||
16 | protected $config; |
||
17 | |||
18 | public function setConfig(Configuration $config): void |
||
19 | 3 | { |
|
20 | $this->config = $config; |
||
21 | 3 | } |
|
22 | 3 | ||
23 | /** @var array<string, string> */ |
||
24 | private static $alignments = [ |
||
25 | 'left' => ':--', |
||
26 | 'right' => '--:', |
||
27 | 'center' => ':-:', |
||
28 | ]; |
||
29 | |||
30 | /** @var array<int, string>|null */ |
||
31 | private $columnAlignments = []; |
||
32 | |||
33 | /** @var string|null */ |
||
34 | private $caption = null; |
||
35 | |||
36 | public function preConvert(ElementInterface $element): void |
||
37 | { |
||
38 | $tag = $element->getTagName(); |
||
39 | // Only table cells and caption are allowed to contain content. |
||
40 | // Remove all text between other table elements. |
||
41 | if ($tag === 'th' || $tag === 'td' || $tag === 'caption') { |
||
42 | return; |
||
43 | } |
||
44 | |||
45 | foreach ($element->getChildren() as $child) { |
||
46 | if ($child->isText()) { |
||
47 | $child->setFinalMarkdown(''); |
||
48 | 3 | } |
|
49 | } |
||
50 | 3 | } |
|
51 | |||
52 | public function convert(ElementInterface $element): string |
||
53 | 3 | { |
|
54 | 3 | $value = $element->getValue(); |
|
55 | 3 | ||
56 | 3 | switch ($element->getTagName()) { |
|
57 | 2 | case 'table': |
|
58 | 2 | $this->columnAlignments = []; |
|
59 | 2 | if ($this->caption) { |
|
60 | 3 | $side = $this->config->getOption('table_caption_side'); |
|
61 | if ($side === 'top') { |
||
62 | $value = $this->caption . "\n" . $value; |
||
63 | } elseif ($side === 'bottom') { |
||
64 | $value .= $this->caption; |
||
65 | } |
||
66 | |||
67 | 3 | $this->caption = null; |
|
68 | } |
||
69 | 3 | ||
70 | return $value . "\n"; |
||
71 | 3 | case 'caption': |
|
72 | 3 | $this->caption = \trim($value); |
|
73 | 3 | ||
74 | 3 | return ''; |
|
75 | 3 | case 'tr': |
|
76 | 3 | $value .= "|\n"; |
|
77 | 3 | if ($this->columnAlignments !== null) { |
|
78 | 3 | $value .= '|' . \implode('|', $this->columnAlignments) . "|\n"; |
|
79 | 3 | ||
80 | 2 | $this->columnAlignments = null; |
|
81 | 3 | } |
|
82 | 2 | ||
83 | 3 | return $value; |
|
84 | 3 | case 'th': |
|
85 | 3 | case 'td': |
|
86 | 3 | if ($this->columnAlignments !== null) { |
|
87 | 3 | $align = $element->getAttribute('align'); |
|
88 | 3 | ||
89 | 3 | $this->columnAlignments[] = self::$alignments[$align] ?? '---'; |
|
90 | 3 | } |
|
91 | 3 | ||
92 | 2 | $value = \str_replace("\n", ' ', $value); |
|
93 | 3 | $value = \str_replace('|', Coerce::toString($this->config->getOption('table_pipe_escape') ?? '\|'), $value); |
|
94 | 3 | ||
95 | 3 | return '| ' . \trim($value) . ' '; |
|
96 | 3 | case 'thead': |
|
97 | 3 | case 'tbody': |
|
98 | 3 | case 'tfoot': |
|
99 | 2 | case 'colgroup': |
|
100 | 3 | case 'col': |
|
101 | 3 | return $value; |
|
102 | 3 | default: |
|
103 | 3 | return ''; |
|
104 | 3 | } |
|
105 | 3 | } |
|
106 | 3 | ||
107 | 3 | /** |
|
108 | 3 | * @return string[] |
|
109 | */ |
||
110 | public function getSupportedTags(): array |
||
111 | { |
||
112 | return ['table', 'tr', 'th', 'td', 'thead', 'tbody', 'tfoot', 'colgroup', 'col', 'caption']; |
||
113 | } |
||
114 | } |
||
115 |