13440a8c0SAndreas Gohr<?php 23440a8c0SAndreas Gohr 33440a8c0SAndreas Gohrnamespace dokuwiki\test\Parsing\Markdown; 43440a8c0SAndreas Gohr 53440a8c0SAndreas Gohruse Doku_Renderer_xhtml; 63440a8c0SAndreas Gohr 73440a8c0SAndreas Gohr/** 83440a8c0SAndreas Gohr * XHTML renderer tuned to emit the minimal HTML shape GFM's spec.txt uses. 93440a8c0SAndreas Gohr * 103440a8c0SAndreas Gohr * DokuWiki's production XHTML renderer wraps internal media in details 113440a8c0SAndreas Gohr * links pointing at `/lib/exe/fetch.php?media=...` / `/lib/exe/detail.php?media=...`, 123440a8c0SAndreas Gohr * rewrites internal link hrefs to `/doku.php?id=...`, and adds wiki-specific 133440a8c0SAndreas Gohr * classes and attributes. All of this is correct for live wiki pages but 143440a8c0SAndreas Gohr * diverges byte-for-byte from GFM's bare `<img src="...">` and 153440a8c0SAndreas Gohr * `<a href="...">...</a>`. 163440a8c0SAndreas Gohr * 173440a8c0SAndreas Gohr * This renderer is used only by {@see GfmSpecTest} so the spec roundtrip 183440a8c0SAndreas Gohr * can compare against byte-level spec HTML. Production rendering is 193440a8c0SAndreas Gohr * unchanged. Methods not overridden here fall through to the XHTML 203440a8c0SAndreas Gohr * renderer (paragraphs, emphasis, code spans, lists, etc.) — those render 213440a8c0SAndreas Gohr * the same shape the spec expects. 223440a8c0SAndreas Gohr * 233440a8c0SAndreas Gohr * Note: title attributes on links/images are discarded at handle time 243440a8c0SAndreas Gohr * (no DW instruction slot), so spec examples that expect `title="..."` 253440a8c0SAndreas Gohr * still don't pass and stay in `skip.php`. 263440a8c0SAndreas Gohr */ 273440a8c0SAndreas Gohrclass SpecCompatRenderer extends Doku_Renderer_xhtml 283440a8c0SAndreas Gohr{ 29*b1c59bedSAndreas Gohr 303440a8c0SAndreas Gohr public function internalmedia( 313440a8c0SAndreas Gohr $src, 323440a8c0SAndreas Gohr $title = null, 333440a8c0SAndreas Gohr $align = null, 343440a8c0SAndreas Gohr $width = null, 353440a8c0SAndreas Gohr $height = null, 363440a8c0SAndreas Gohr $cache = null, 373440a8c0SAndreas Gohr $linking = null, 383440a8c0SAndreas Gohr $return = false 393440a8c0SAndreas Gohr ) { 403440a8c0SAndreas Gohr $this->doc .= $this->specImg($src, $title, $width, $height); 413440a8c0SAndreas Gohr } 423440a8c0SAndreas Gohr 433440a8c0SAndreas Gohr public function externalmedia( 443440a8c0SAndreas Gohr $src, 453440a8c0SAndreas Gohr $title = null, 463440a8c0SAndreas Gohr $align = null, 473440a8c0SAndreas Gohr $width = null, 483440a8c0SAndreas Gohr $height = null, 493440a8c0SAndreas Gohr $cache = null, 503440a8c0SAndreas Gohr $linking = null, 513440a8c0SAndreas Gohr $return = false 523440a8c0SAndreas Gohr ) { 533440a8c0SAndreas Gohr $this->doc .= $this->specImg($src, $title, $width, $height); 543440a8c0SAndreas Gohr } 553440a8c0SAndreas Gohr 563440a8c0SAndreas Gohr public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') 573440a8c0SAndreas Gohr { 583440a8c0SAndreas Gohr $this->doc .= $this->specLink($id, $name); 593440a8c0SAndreas Gohr } 603440a8c0SAndreas Gohr 613440a8c0SAndreas Gohr public function externallink($url, $name = null, $returnonly = false) 623440a8c0SAndreas Gohr { 633440a8c0SAndreas Gohr $this->doc .= $this->specLink($url, $name); 643440a8c0SAndreas Gohr } 653440a8c0SAndreas Gohr 663440a8c0SAndreas Gohr public function interwikilink($match, $name, $wikiName, $wikiUri, $returnonly = false) 673440a8c0SAndreas Gohr { 683440a8c0SAndreas Gohr // Spec has no interwiki expectations; emit the raw `wp>Page` form as 693440a8c0SAndreas Gohr // href so the mode is still visible but obviously non-standard. 703440a8c0SAndreas Gohr $this->doc .= $this->specLink($match, $name); 713440a8c0SAndreas Gohr } 723440a8c0SAndreas Gohr 733440a8c0SAndreas Gohr public function emaillink($address, $name = null, $returnonly = false) 743440a8c0SAndreas Gohr { 753440a8c0SAndreas Gohr $this->doc .= $this->specLink('mailto:' . $address, $name ?? $address); 763440a8c0SAndreas Gohr } 773440a8c0SAndreas Gohr 783440a8c0SAndreas Gohr public function locallink($hash, $name = null, $returnonly = false) 793440a8c0SAndreas Gohr { 803440a8c0SAndreas Gohr $this->doc .= $this->specLink('#' . $hash, $name ?? $hash); 813440a8c0SAndreas Gohr } 823440a8c0SAndreas Gohr 833440a8c0SAndreas Gohr public function windowssharelink($url, $name = null, $returnonly = false) 843440a8c0SAndreas Gohr { 853440a8c0SAndreas Gohr $this->doc .= $this->specLink($url, $name); 863440a8c0SAndreas Gohr } 873440a8c0SAndreas Gohr 88*b1c59bedSAndreas Gohr public function code($text, $language = null, $filename = null, $options = null) 89*b1c59bedSAndreas Gohr { 90*b1c59bedSAndreas Gohr $this->doc .= $this->specCode($text, $language); 91*b1c59bedSAndreas Gohr } 92*b1c59bedSAndreas Gohr 93*b1c59bedSAndreas Gohr public function file($text, $language = null, $filename = null, $options = null) 94*b1c59bedSAndreas Gohr { 95*b1c59bedSAndreas Gohr $this->doc .= $this->specCode($text, $language); 96*b1c59bedSAndreas Gohr } 97*b1c59bedSAndreas Gohr 98*b1c59bedSAndreas Gohr public function preformatted($text) 99*b1c59bedSAndreas Gohr { 100*b1c59bedSAndreas Gohr // The Preformatted CallWriter rewriter collapses start/content/ 101*b1c59bedSAndreas Gohr // newline/end into one `preformatted` call. GFM expects the body 102*b1c59bedSAndreas Gohr // to end with a newline (spec example 104); DW's internal text 103*b1c59bedSAndreas Gohr // loses it to `trim()`, so we re-append here. 104*b1c59bedSAndreas Gohr $this->doc .= $this->specCode($text . "\n", null); 105*b1c59bedSAndreas Gohr } 106*b1c59bedSAndreas Gohr 107*b1c59bedSAndreas Gohr /** 108*b1c59bedSAndreas Gohr * GFM shape: <pre><code class="language-xxx">...</code></pre>. The 109*b1c59bedSAndreas Gohr * production DW renderer emits <pre class="code"> with no inner 110*b1c59bedSAndreas Gohr * <code>, which diverges byte-for-byte. 111*b1c59bedSAndreas Gohr */ 112*b1c59bedSAndreas Gohr private function specCode($text, $language): string 113*b1c59bedSAndreas Gohr { 114*b1c59bedSAndreas Gohr $classAttr = ''; 115*b1c59bedSAndreas Gohr if ($language !== null && $language !== '') { 116*b1c59bedSAndreas Gohr $classAttr = ' class="language-' . hsc((string) $language) . '"'; 117*b1c59bedSAndreas Gohr } 118*b1c59bedSAndreas Gohr return '<pre><code' . $classAttr . '>' . hsc((string) $text) . '</code></pre>'; 119*b1c59bedSAndreas Gohr } 120*b1c59bedSAndreas Gohr 1213440a8c0SAndreas Gohr private function specImg($src, $alt, $width, $height): string 1223440a8c0SAndreas Gohr { 1233440a8c0SAndreas Gohr $out = '<img src="' . hsc((string) $src) . '"'; 1243440a8c0SAndreas Gohr $out .= ' alt="' . hsc((string) $alt) . '"'; 1253440a8c0SAndreas Gohr if ($width !== null) $out .= ' width="' . (int) $width . '"'; 1263440a8c0SAndreas Gohr if ($height !== null) $out .= ' height="' . (int) $height . '"'; 1273440a8c0SAndreas Gohr $out .= ' />'; 1283440a8c0SAndreas Gohr return $out; 1293440a8c0SAndreas Gohr } 1303440a8c0SAndreas Gohr 1313440a8c0SAndreas Gohr /** 1323440a8c0SAndreas Gohr * Emit a bare <a href="...">label</a>. If the label is a media 1333440a8c0SAndreas Gohr * descriptor array (the shape Media::parseMedia() returns, passed by 1343440a8c0SAndreas Gohr * Internallink / GfmLink when the label is `{{img}}` / ``), 1353440a8c0SAndreas Gohr * render the <img> inside the <a>. 1363440a8c0SAndreas Gohr */ 1373440a8c0SAndreas Gohr private function specLink($href, $label): string 1383440a8c0SAndreas Gohr { 1393440a8c0SAndreas Gohr if (is_array($label) && isset($label['type'])) { 1403440a8c0SAndreas Gohr $img = $this->specImg( 1413440a8c0SAndreas Gohr $label['src'], 1423440a8c0SAndreas Gohr $label['title'], 1433440a8c0SAndreas Gohr $label['width'] ?? null, 1443440a8c0SAndreas Gohr $label['height'] ?? null 1453440a8c0SAndreas Gohr ); 1463440a8c0SAndreas Gohr return '<a href="' . hsc((string) $href) . '">' . $img . '</a>'; 1473440a8c0SAndreas Gohr } 1483440a8c0SAndreas Gohr $text = ($label === null || $label === '') ? $href : $label; 1493440a8c0SAndreas Gohr return '<a href="' . hsc((string) $href) . '">' . hsc((string) $text) . '</a>'; 1503440a8c0SAndreas Gohr } 1513440a8c0SAndreas Gohr} 152