` and * `...`. * * This renderer is used only by {@see GfmSpecTest} so the spec roundtrip * can compare against byte-level spec HTML. Production rendering is * unchanged. Methods not overridden here fall through to the XHTML * renderer (paragraphs, emphasis, code spans, lists, etc.) — those render * the same shape the spec expects. * * Note: title attributes on links/images are discarded at handle time * (no DW instruction slot), so spec examples that expect `title="..."` * still don't pass and stay in `skip.php`. */ class SpecCompatRenderer extends Doku_Renderer_xhtml { public function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) { // Production DW wraps `` in `
`; // the spec expects bare `
`. $this->doc .= "
\n"; } public function table_close($pos = null) { // Drop the matching `` from the production wrapper. $this->doc .= "
"; } public function tablerow_open($classes = null) { // Strip DW's `class="rowN"` row counter — spec rows have no class. $this->doc .= "\n"; } public function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { // Production DW emits alignment as `class="...align"`; the spec uses // an `align="..."` attribute. Drop the `class="colN"` counter too. $this->doc .= 'alignAttr($align) . '>'; } public function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { $this->doc .= 'alignAttr($align) . '>'; } private function alignAttr(?string $align): string { if ($align === null) return ''; return ' align="' . $align . '"'; } public function internalmedia( $src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null, $return = false ) { $this->doc .= $this->specImg($src, $title, $width, $height); } public function externalmedia( $src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null, $return = false ) { $this->doc .= $this->specImg($src, $title, $width, $height); } public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { $this->doc .= $this->specLink($id, $name); } public function externallink($url, $name = null, $returnonly = false) { $this->doc .= $this->specLink($url, $name); } public function interwikilink($match, $name, $wikiName, $wikiUri, $returnonly = false) { // Spec has no interwiki expectations; emit the raw `wp>Page` form as // href so the mode is still visible but obviously non-standard. $this->doc .= $this->specLink($match, $name); } public function emaillink($address, $name = null, $returnonly = false) { $this->doc .= $this->specLink('mailto:' . $address, $name ?? $address); } public function locallink($hash, $name = null, $returnonly = false) { $this->doc .= $this->specLink('#' . $hash, $name ?? $hash); } public function windowssharelink($url, $name = null, $returnonly = false) { $this->doc .= $this->specLink($url, $name); } public function code($text, $language = null, $filename = null, $options = null) { $this->doc .= $this->specCode($text, $language); } public function listu_open($classes = null) { $this->doc .= "\n"; } public function listo_open($classes = null, $start = 1) { if ((int) $start !== 1) { $this->doc .= '
    \n"; } else { $this->doc .= "
      \n"; } } public function listo_close() { $this->doc .= "
    \n"; } public function listitem_open($level, $node = false) { $this->doc .= '
  1. '; } public function listitem_close() { $this->doc .= "
  2. \n"; } public function listcontent_open() { // GFM has no per-item content wrapper - tight items put text directly // inside
  3. , loose items wrap it in

    . The handler emits/strips // p_open / p_close to drive that distinction; the wrapper itself // produces no output here. } public function listcontent_close() { } public function file($text, $language = null, $filename = null, $options = null) { $this->doc .= $this->specCode($text, $language); } public function preformatted($text) { // The Preformatted CallWriter rewriter collapses start/content/ // newline/end into one `preformatted` call. GFM expects the body // to end with a newline (spec example 104); DW's internal text // loses it to `trim()`, so we re-append here. $this->doc .= $this->specCode($text . "\n", null); } /** * GFM shape:

    ...
    . The * production DW renderer emits
     with no inner
         * , which diverges byte-for-byte.
         */
        private function specCode($text, $language): string
        {
            $classAttr = '';
            if ($language !== null && $language !== '') {
                $classAttr = ' class="language-' . hsc((string) $language) . '"';
            }
            return '
    ' . hsc((string) $text) . '
    '; } private function specImg($src, $alt, $width, $height): string { $out = 'label. If the label is a media * descriptor array (the shape Media::parseMedia() returns, passed by * Internallink / GfmLink when the label is `{{img}}` / `![alt](img)`), * render the inside the . */ private function specLink($href, $label): string { if (is_array($label) && isset($label['type'])) { $img = $this->specImg( $label['src'], $label['title'], $label['width'] ?? null, $label['height'] ?? null ); return '' . $img . ''; } $text = ($label === null || $label === '') ? $href : $label; return '' . hsc((string) $text) . ''; } }