Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions apidoc/PdfGuideRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,21 @@ public static function normalizeMarkdown($markdown)

foreach ($lines as $line) {
if (preg_match('~^([ \t]*(?:>[ \t]*)*)```(?:[a-zA-Z0-9_+.-]+)?[ \t]*$~', $line, $matches)) {
// The legacy LaTeX parser handles indentation around fences
// inconsistently: it may recognize one marker as a fence and
// render its pair as an inline backtick. Keep quote depth but
// canonicalize whitespace before both fence markers.
$prefix = str_repeat('> ', substr_count($matches[1], '>'));
$line = $prefix . ltrim(substr($line, strlen($matches[1])));

if ($fencePrefix === null) {
$prefix = $matches[1];
$previousLine = end($result);
$previousContent = preg_replace('~^' . preg_quote($prefix, '~') . '~', '', $previousLine);
if ($previousLine !== false && trim($previousContent) !== '') {
$result[] = rtrim($prefix);
}
$fencePrefix = $prefix;
} elseif ($matches[1] === $fencePrefix) {
} elseif ($prefix === $fencePrefix) {
$fencePrefix = null;
}
}
Expand Down Expand Up @@ -135,7 +141,7 @@ static function (array $matches) {
// table failed on cells such as < and <= with "Missing Pygments output".
// These are plain-text cells, so \detokenize preserves their appearance
// and literal characters without the fragile external invocation.
return preg_replace_callback(
$latex = preg_replace_callback(
'~\\\\begin\{tabularx\}.*?\\\\end\{tabularx\}~s',
static function (array $table) {
return preg_replace(
Expand All @@ -146,5 +152,22 @@ static function (array $table) {
},
$latex
);

// A malformed fenced block may make the defense-in-depth replacement
// above span subsequent API links and unescape their property sigils.
// An unescaped dollar starts TeX math mode and eventually aborts the
// PDF build with "Extra }, or forgotten $". Match balanced braces
// because API labels contain nested \allowbreak{} commands.
return preg_replace_callback(
'~\\\\texttt(?<body>\{(?:[^{}]++|(?&body))*\})~',
static function (array $text) {
if (strpos($text[0], '\\detokenize{') !== false) {
return $text[0];
}

return preg_replace('~(?<!\\\\)\$~', '\\\\$', $text[0]);
},
$latex
);
}
}
34 changes: 34 additions & 0 deletions tests/unit/PdfGuideRendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ public function testAddsQuotedBlankLineBeforeFencedCodeBlockInBlockquote()
self::assertSame($expected, PdfGuideRenderer::normalizeMarkdown($markdown));
}

public function testNormalizesFenceIndentation()
{
$markdown = " Paragraph\n ```php\ncode();\n ```\n";
$expected = " Paragraph\n\n```php\ncode();\n```\n";

self::assertSame($expected, PdfGuideRenderer::normalizeMarkdown($markdown));
}

public function testNormalizesFenceIndentationInBlockquote()
{
$markdown = "> Example\n> ```php\n> code();\n> ```\n";
$expected = "> Example\n>\n> ```php\n> code();\n> ```\n";

self::assertSame($expected, PdfGuideRenderer::normalizeMarkdown($markdown));
}

public function testPreservesCyrillicText()
{
$markdown = "Иерархия и данные в файлах.\n";
Expand Down Expand Up @@ -59,6 +75,7 @@ public function testReplacesInlineMintedInsideTableOnly()
\begin{tabularx}{\textwidth}{|c|c|}
\mintinline{text}{lt} & \mintinline{text}{<}\\ \hline
\mintinline{text}{lte} & \mintinline{text}{<=}\\ \hline
\mintinline{text}{property} & \mintinline{text}{$property}\\ \hline
\end{tabularx}
LATEX;

Expand All @@ -67,7 +84,24 @@ public function testReplacesInlineMintedInsideTableOnly()
\begin{tabularx}{\textwidth}{|c|c|}
\texttt{\detokenize{lt}} & \texttt{\detokenize{<}}\\ \hline
\texttt{\detokenize{lte}} & \texttt{\detokenize{<=}}\\ \hline
\texttt{\detokenize{property}} & \texttt{\detokenize{$property}}\\ \hline
\end{tabularx}
LATEX;

self::assertSame($expected, PdfGuideRenderer::normalizeLatex($latex));
}

public function testEscapesPropertySigilInApiLink()
{
$latex = <<<'LATEX'
\texttt{yii\allowbreak{}::\allowbreak{}$property}
\texttt{$otherProperty}
\texttt{already escaped: \$property}
LATEX;
$expected = <<<'LATEX'
\texttt{yii\allowbreak{}::\allowbreak{}\$property}
\texttt{\$otherProperty}
\texttt{already escaped: \$property}
LATEX;

self::assertSame($expected, PdfGuideRenderer::normalizeLatex($latex));
Expand Down