fix: separate block elements from the inline content before them
An image or link right before a heading or paragraph ended up on the same line, e.g. `### A`. Fixes #259
This commit is contained in:
1 parent
6b7a074e8d
commit
1bb05d0606
2 files changed
+29
-3
No files matched your search
+25
-3
@@ -16,6 +16,11 @@ namespace League\HTMLToMarkdown;
|
||||
*/
|
||||
class HtmlConverter implements HtmlConverterInterface
|
||||
{
|
||||
/**
|
||||
* Blocks which start a new paragraph when they come right after inline content
|
||||
*/
|
||||
private const SEPARATED_BLOCK_TAGS = ['blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'p', 'pre'];
|
||||
|
||||
/** @var Environment */
|
||||
protected $environment;
|
||||
|
||||
@@ -173,13 +178,17 @@ class HtmlConverter implements HtmlConverterInterface
|
||||
*
|
||||
* Finds children of each node and convert those to #text nodes containing their Markdown equivalent,
|
||||
* starting with the innermost element and working up to the outermost element.
|
||||
*
|
||||
* @param bool $followsInlineContent Whether the element comes right after inline content, like an image, on the same line
|
||||
*
|
||||
* @return string The Markdown the element was replaced with
|
||||
*/
|
||||
private function convertChildren(ElementInterface $element): void
|
||||
private function convertChildren(ElementInterface $element, bool $followsInlineContent = false): string
|
||||
{
|
||||
// Don't convert HTML code inside <code> and <pre> blocks to Markdown - that should stay as HTML
|
||||
// except if the current node is a code tag, which needs to be converted by the CodeConverter.
|
||||
if ($element->isDescendantOf(['pre', 'code']) && $element->getTagName() !== 'code') {
|
||||
return;
|
||||
return '';
|
||||
}
|
||||
|
||||
// Give converter a chance to inspect/modify the DOM before children are converted
|
||||
@@ -190,18 +199,31 @@ class HtmlConverter implements HtmlConverterInterface
|
||||
|
||||
// If the node has children, convert those to Markdown first
|
||||
if ($element->hasChildren()) {
|
||||
$childFollowsInlineContent = false;
|
||||
foreach ($element->getChildren() as $child) {
|
||||
$this->convertChildren($child);
|
||||
$childMarkdown = $this->convertChildren($child, $childFollowsInlineContent);
|
||||
if ($child->getTagName() === '#comment' || \trim($childMarkdown, ' ') === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childFollowsInlineContent = ! $child->isBlock() && \substr(\rtrim($childMarkdown, ' '), -1) !== "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Now that child nodes have been converted, convert the original node
|
||||
$markdown = $this->convertToMarkdown($element);
|
||||
|
||||
// These blocks only add newlines after themselves, so one that follows inline content, like an image, needs them before it too
|
||||
if ($followsInlineContent && \in_array($element->getTagName(), self::SEPARATED_BLOCK_TAGS, true) && \trim($markdown) !== '') {
|
||||
$markdown = "\n\n" . $markdown;
|
||||
}
|
||||
|
||||
// Create a DOM text node containing the Markdown equivalent of the original node
|
||||
|
||||
// Replace the old $node e.g. '<h3>Title</h3>' with the new $markdown_node e.g. '### Title'
|
||||
$element->setFinalMarkdown($markdown);
|
||||
|
||||
return $markdown;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user