xref: /dokuwiki/_test/tests/Parsing/Markdown/SpecCompatRenderer.php (revision b1c59bed2e3645a1f5f11438cdbe7d1596f4a3a4)
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}}` / `![alt](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