1 | <?php |
||
2 | /** |
||
3 | * @copyright Copyright (c) 2014 Carsten Brandt |
||
4 | * @license https://github.com/cebe/markdown/blob/master/LICENSE |
||
5 | * @link https://github.com/cebe/markdown#readme |
||
6 | */ |
||
7 | |||
8 | namespace cebe\markdown\latex; |
||
9 | |||
10 | use cebe\markdown\block\FencedCodeTrait; |
||
11 | use cebe\markdown\block\TableTrait; |
||
12 | use cebe\markdown\inline\StrikeoutTrait; |
||
13 | use cebe\markdown\inline\UrlLinkTrait; |
||
14 | |||
15 | /** |
||
16 | * Markdown parser for github flavored markdown. |
||
17 | * |
||
18 | * - uses the [tabularx](http://www.ctan.org/pkg/tabularx) environment for tables. |
||
19 | * |
||
20 | * @author Carsten Brandt <[email protected]> |
||
21 | */ |
||
22 | class GithubMarkdown extends Markdown |
||
23 | { |
||
24 | // include block element parsing using traits |
||
25 | use TableTrait; |
||
26 | use FencedCodeTrait; |
||
27 | |||
28 | // include inline element parsing using traits |
||
29 | use StrikeoutTrait; |
||
30 | use UrlLinkTrait; |
||
31 | |||
32 | /** |
||
33 | * @var boolean whether to interpret newlines as `<br />`-tags. |
||
34 | * This feature is useful for comments where newlines are often meant to be real new lines. |
||
35 | */ |
||
36 | public $enableNewlines = false; |
||
37 | |||
38 | /** |
||
39 | * @inheritDoc |
||
40 | */ |
||
41 | protected $escapeCharacters = [ |
||
42 | // from Markdown |
||
43 | '\\', // backslash |
||
44 | '`', // backtick |
||
45 | '*', // asterisk |
||
46 | '_', // underscore |
||
47 | '{', '}', // curly braces |
||
48 | '[', ']', // square brackets |
||
49 | '(', ')', // parentheses |
||
50 | '#', // hash mark |
||
51 | '+', // plus sign |
||
52 | '-', // minus sign (hyphen) |
||
53 | '.', // dot |
||
54 | '!', // exclamation mark |
||
55 | '<', '>', |
||
56 | // added by GithubMarkdown |
||
57 | ':', // colon |
||
58 | '|', // pipe |
||
59 | ]; |
||
60 | |||
61 | |||
62 | /** |
||
63 | * Consume lines for a paragraph |
||
64 | * |
||
65 | * Allow headlines, lists and code to break paragraphs |
||
66 | */ |
||
67 | 13 | protected function consumeParagraph($lines, $current) |
|
68 | { |
||
69 | // consume until newline |
||
70 | 13 | $content = []; |
|
71 | 13 | for ($i = $current, $count = count($lines); $i < $count; $i++) { |
|
72 | 13 | $line = $lines[$i]; |
|
73 | 13 | if (!empty($line) && ltrim($line) !== '' && |
|
74 | 13 | !($line[0] === "\t" || $line[0] === " " && strncmp($line, ' ', 4) === 0) && |
|
75 | 13 | !$this->identifyHeadline($line, $lines, $i) && |
|
76 | 13 | !$this->identifyUl($line, $lines, $i) && |
|
77 | 13 | !$this->identifyOl($line, $lines, $i)) |
|
78 | 13 | { |
|
79 | 13 | $content[] = $line; |
|
80 | 13 | } else { |
|
81 | 11 | break; |
|
82 | } |
||
83 | 13 | } |
|
84 | $block = [ |
||
85 | 13 | 'paragraph', |
|
86 | 13 | 'content' => $this->parseInline(implode("\n", $content)), |
|
87 | 13 | ]; |
|
88 | 13 | return [$block, --$i]; |
|
89 | } |
||
90 | |||
91 | /** |
||
92 | * @inheritdoc |
||
93 | */ |
||
94 | 2 | protected function renderCode($block) |
|
95 | { |
||
96 | // make sure this is not replaced by the trait |
||
97 | 2 | return parent::renderCode($block); |
|
98 | } |
||
99 | |||
100 | /** |
||
101 | * @inheritdoc |
||
102 | */ |
||
103 | 2 | protected function renderAutoUrl($block) |
|
104 | { |
||
105 | 2 | return '\url{' . $this->escapeUrl($block[1]) . '}'; |
|
106 | } |
||
107 | |||
108 | /** |
||
109 | * @inheritdoc |
||
110 | */ |
||
111 | 1 | protected function renderStrike($block) |
|
112 | { |
||
113 | 1 | return '\sout{' . $this->renderAbsy($block[1]) . '}'; |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * @inheritdocs |
||
118 | * |
||
119 | * Parses a newline indicated by two spaces on the end of a markdown line. |
||
120 | */ |
||
121 | 13 | protected function renderText($text) |
|
122 | { |
||
123 | 13 | if ($this->enableNewlines) { |
|
124 | 1 | return preg_replace("/( \n|\n)/", "\\\\\\\\\n", $this->escapeLatex($text[1])); |
|
125 | } else { |
||
126 | 13 | return parent::renderText($text); |
|
127 | } |
||
128 | } |
||
129 | |||
130 | private $_tableCellHead = false; |
||
131 | private $_tds = 0; |
||
132 | |||
133 | 1 | protected function renderTable($block) |
|
134 | { |
||
135 | 1 | $align = []; |
|
136 | 1 | foreach($block['cols'] as $col) { |
|
137 | 1 | if (empty($col)) { |
|
138 | 1 | $align[] = 'X'; |
|
139 | 1 | } else { |
|
140 | 1 | $align[] = $col[0]; |
|
141 | } |
||
142 | 1 | } |
|
143 | 1 | $align = implode('|', $align); |
|
144 | |||
145 | 1 | $content = ''; |
|
146 | 1 | $first = true; |
|
147 | 1 | $numThs = 0; |
|
148 | 1 | foreach($block['rows'] as $row) { |
|
149 | 1 | $this->_tableCellHead = $first; |
|
150 | 1 | $this->_tds = 0; |
|
151 | 1 | $content .= $this->renderAbsy($this->parseInline($row)); // TODO move this to the consume step |
|
152 | 1 | if ($first) { |
|
153 | 1 | $numThs = $this->_tds; |
|
154 | 1 | } else { |
|
155 | 1 | while ($this->_tds < $numThs) { |
|
156 | $content .= ' & '; |
||
157 | $this->_tds++; |
||
158 | } |
||
159 | } |
||
160 | 1 | $content .= "\\\\ \\hline\n"; |
|
161 | 1 | $first = false; |
|
162 | 1 | } |
|
163 | 1 | return "\n\\noindent\\begin{tabularx}{\\textwidth}{|$align|}\\hline\n$content\\end{tabularx}\n\n"; |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * @marker | |
||
168 | */ |
||
169 | 1 | protected function parseTd($markdown) |
|
170 | { |
||
171 | 1 | if (isset($this->context[1]) && $this->context[1] === 'table') { |
|
172 | 1 | $this->_tds++; |
|
173 | 1 | return [['tableSep'], 1]; |
|
174 | } |
||
175 | 1 | return [['text', $markdown[0]], 1]; |
|
176 | } |
||
177 | |||
178 | 1 | protected function renderTableSep($block) |
|
179 | { |
||
180 | 1 | return '&'; |
|
181 | } |
||
182 | } |
||
183 |