erusev / parsedown

Better Markdown Parser in PHP
https://parsedown.org
MIT License
14.74k stars 1.12k forks source link

With inlineLink I get both images and anchor tags #711

Closed jenstornell closed 5 years ago

jenstornell commented 5 years ago

Today I tried to extend Parsedown. The image example worked just as expected but to prefix an anchor tag (which was my goal) did not go as well.

Content (shorten down):

Some text

![My image](my-image.png)

Some more text

[A link](a-link)

The problem is that inlineLink will find both my image and my anchor tag. I can't by the result know if it's an image or an anchor tag.

class ParsedownExtension extends Parsedown {
  protected function inlineLink($excerpt) {
    $link = parent::inlineLink($excerpt);
    if(!isset($link)) return null;

    print_r($link);

    return $link;
  }
}
Array
(
    [extent] => 56
    [element] => Array
        (
            [name] => a
            [handler] => Array
                (
                    [function] => lineElements
                    [argument] => My image
                    [destination] => elements
                )

            [nonNestables] => Array
                (
                    [0] => Url
                    [1] => Link
                )

            [attributes] => Array
                (
                    [href] => my-image.png
                    [title] => 
                )
        )
)

Array
(
    [extent] => 20
    [element] => Array
        (
            [name] => a
            [handler] => Array
                (
                    [function] => lineElements
                    [argument] => A link
                    [destination] => elements
                )

            [nonNestables] => Array
                (
                    [0] => Url
                    [1] => Link
                )

            [attributes] => Array
                (
                    [href] => a-link
                    [title] => 
                )
        )
)
aidantwoods commented 5 years ago

You're trying to remove link parsing, but keep images if I'm reading that right?

jenstornell commented 5 years ago

@aidantwoods

I have one method inlineImage where I manipulate an image from my-image.jpg to https://example.com/cache/assets/images/my-image-800.jpg (prefix with a cache path and suffix with an image width).

For the anchor tag I intend to do something similar but simpler, just prefix my-link to https://example.com/my-link.

So it's a difference between the two cases. I hope that answered your question.

jenstornell commented 5 years ago

I even tried to add $image['element']['type'] = 'image'; in my inlineImage, but that does not work because inlineLink is always loaded first (by inlineImage) so it will not be aware of it anyway.

aidantwoods commented 5 years ago

There isn't such a good way to solve this in the current version since Parsedown's image will defer to $this->inlineLink, which is of course your overloaded version.

In 2.0 you'll be able to swap out components without this being an issue, since if any deferring exists it defers statically and won't be affected by other loaded components.

If you're wanting to do this I think you'll have to completely overload and replicate the functionality in the inlineImage method, but replacing its call to $this->inlineLink with parent::inlineLink to defer Parsedown's version, then add your own functionality after. You should then be free to separately overload inlineLink rendering.

jenstornell commented 5 years ago

@aidantwoods Yes, I just figured that out as a last resort, to completely override the function. For my need it should work fine.

I'll close it, as I got a decent workaround and because you will treat it differently in version 2.0.

Thanks for the help!

jenstornell commented 5 years ago

@aidantwoods

For completeness, here is the full code that I use now that is working.

class ParsedownExtension extends Parsedown {
  protected function inlineImage($Excerpt)
  {
      if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
      {
          return;
      }

      $Excerpt['text']= substr($Excerpt['text'], 1);

      // Custom
      $Link = parent::inlineLink($Excerpt);

      if ($Link === null)
      {
          return;
      }

      $Inline = array(
          'extent' => $Link['extent'] + 1,
          'element' => array(
              'name' => 'img',
              'attributes' => array(
                  'src' => $Link['element']['attributes']['href'],
                  'alt' => $Link['element']['handler']['argument'],
              ),
              'autobreak' => true,
          ),
      );

      $Inline['element']['attributes'] += $Link['element']['attributes'];

      unset($Inline['element']['attributes']['href']);

      // Custom
      $pathinfo = pathinfo($Inline['element']['attributes']['src']);
      $Inline['element']['attributes']['src'] = $this->image_root . '/' . $pathinfo['filename'] . '-' . $this->image_suffix . '.' . $pathinfo['extension'];

      return $Inline;
  }

  protected function inlineLink($excerpt) {
    $link = parent::inlineLink($excerpt);
    if(!isset($link)) return null;

    if(substr($link['element']['attributes']['href'], 0, 4) != 'http') {
      $link['element']['attributes']['href'] = $this->url_root . '/' . $link['element']['attributes']['href'];
    }
    return $link;
  }
}
$Parsedown = new ParsedownExtension();
$Parsedown->url_root = 'http://example.com';
$Parsedown->image_root = 'http://example.com/assets/images/some-dir';
$Parsedown->image_suffix = 800;
echo $Parsedown->setBreaksEnabled(true)->text($text);