1<?php 2 3namespace dokuwiki\test\Parsing\Markdown; 4 5use Doku_Renderer_xhtml; 6 7/** 8 * XHTML renderer tuned to emit the minimal HTML shape GFM's spec.txt uses. 9 * 10 * DokuWiki's production XHTML renderer wraps internal media in details 11 * links pointing at `/lib/exe/fetch.php?media=...` / `/lib/exe/detail.php?media=...`, 12 * rewrites internal link hrefs to `/doku.php?id=...`, and adds wiki-specific 13 * classes and attributes. All of this is correct for live wiki pages but 14 * diverges byte-for-byte from GFM's bare `<img src="...">` and 15 * `<a href="...">...</a>`. 16 * 17 * This renderer is used only by {@see GfmSpecTest} so the spec roundtrip 18 * can compare against byte-level spec HTML. Production rendering is 19 * unchanged. Methods not overridden here fall through to the XHTML 20 * renderer (paragraphs, emphasis, code spans, lists, etc.) — those render 21 * the same shape the spec expects. 22 * 23 * Note: title attributes on links/images are discarded at handle time 24 * (no DW instruction slot), so spec examples that expect `title="..."` 25 * still don't pass and stay in `skip.php`. 26 */ 27class SpecCompatRenderer extends Doku_Renderer_xhtml 28{ 29 public function internalmedia( 30 $src, 31 $title = null, 32 $align = null, 33 $width = null, 34 $height = null, 35 $cache = null, 36 $linking = null, 37 $return = false 38 ) { 39 $this->doc .= $this->specImg($src, $title, $width, $height); 40 } 41 42 public function externalmedia( 43 $src, 44 $title = null, 45 $align = null, 46 $width = null, 47 $height = null, 48 $cache = null, 49 $linking = null, 50 $return = false 51 ) { 52 $this->doc .= $this->specImg($src, $title, $width, $height); 53 } 54 55 public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') 56 { 57 $this->doc .= $this->specLink($id, $name); 58 } 59 60 public function externallink($url, $name = null, $returnonly = false) 61 { 62 $this->doc .= $this->specLink($url, $name); 63 } 64 65 public function interwikilink($match, $name, $wikiName, $wikiUri, $returnonly = false) 66 { 67 // Spec has no interwiki expectations; emit the raw `wp>Page` form as 68 // href so the mode is still visible but obviously non-standard. 69 $this->doc .= $this->specLink($match, $name); 70 } 71 72 public function emaillink($address, $name = null, $returnonly = false) 73 { 74 $this->doc .= $this->specLink('mailto:' . $address, $name ?? $address); 75 } 76 77 public function locallink($hash, $name = null, $returnonly = false) 78 { 79 $this->doc .= $this->specLink('#' . $hash, $name ?? $hash); 80 } 81 82 public function windowssharelink($url, $name = null, $returnonly = false) 83 { 84 $this->doc .= $this->specLink($url, $name); 85 } 86 87 private function specImg($src, $alt, $width, $height): string 88 { 89 $out = '<img src="' . hsc((string) $src) . '"'; 90 $out .= ' alt="' . hsc((string) $alt) . '"'; 91 if ($width !== null) $out .= ' width="' . (int) $width . '"'; 92 if ($height !== null) $out .= ' height="' . (int) $height . '"'; 93 $out .= ' />'; 94 return $out; 95 } 96 97 /** 98 * Emit a bare <a href="...">label</a>. If the label is a media 99 * descriptor array (the shape Media::parseMedia() returns, passed by 100 * Internallink / GfmLink when the label is `{{img}}` / ``), 101 * render the <img> inside the <a>. 102 */ 103 private function specLink($href, $label): string 104 { 105 if (is_array($label) && isset($label['type'])) { 106 $img = $this->specImg( 107 $label['src'], 108 $label['title'], 109 $label['width'] ?? null, 110 $label['height'] ?? null 111 ); 112 return '<a href="' . hsc((string) $href) . '">' . $img . '</a>'; 113 } 114 $text = ($label === null || $label === '') ? $href : $label; 115 return '<a href="' . hsc((string) $href) . '">' . hsc((string) $text) . '</a>'; 116 } 117} 118