erusev / parsedown

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

Image Resize via Reference #723

Closed MarkMessa closed 5 years ago

MarkMessa commented 5 years ago

Parsedown provides a way to reference images:

![sometext][image]

[image]: /sample.jpg

If you wanna extend Parsedown to also resize the image, you can just append the following snippet at the end of the main code:

class MyParsedown extends Parsedown
{
    protected function inlineImage($Excerpt)
    {
        $Inline = parent::inlineImage($Excerpt);

        if (!isset($Inline['element']['attributes']['title'])) { return $Inline; }

        $size = $Inline['element']['attributes']['title'];

        if (preg_match('/^\d+x\d+$/', $size)) {
            list($width, $height) = explode('x', $size);

            if($width > 0) $Inline['element']['attributes']['width'] = $width;
            if($height > 0) $Inline['element']['attributes']['height'] = $height;

            unset ($Inline['element']['attributes']['title']);
        }

        return $Inline;
    }
}

Then you can resize images like that:

![sometext][image]

[image]: /sample.jpg '100x100'

Question
There are some situations which would also be helpful the following syntax:

![sometext][image '100x100']

[image]: /sample.jpg

Any idea how to include such feature into Parsedown?

taufik-nurrohman commented 5 years ago

I would do the query string method instead. And use that queries to update the image markup before it is being removed.

$data = $Inline['element']['attributes']['src'];
$a = explode('?', $data, 2); // Result: ['.path/to/image.jpg', 'width=72&height=72']
if (isset($a[1])) {
    parse_str($a[1], $attr);
    $Inline['element']['attributes'] = array_replace($Inline['element']['attributes'], $attr);
    $Inline['element']['attributes']['src'] = $a[0];
}
return $Inline;

Or use literal attribute syntax using my extension like this:

![image](./path/to/image.jpg) {width=72 height=72}
MarkMessa commented 5 years ago

// [.path/to/image.jpg, width=72&height=72]

@tovic Currently, I'm using a modified version of Parsedown (as explained in the OP) that accepts resizing images according the following syntaxes:

1. Inline Image
![Markdown Logo](/md.png "100x100")

2. Reference Image
![Markdown Logo][image]
[image]: /md.png "100x100"

I'm looking for ways to extend such syntax of reference image, to something like:

3. Reference Image Alternative
![Markdown Logo][image "100x100"]
[image]: /md.png

However, if I understood properly, the method you've posted would create an additional syntax for inline images instead for reference images:

![Markdown Logo][/md.png, width=100&height=100]

If this is indeed the case, unfortunately it doesn't address the issue of the OP.

 

Do you agree?

taufik-nurrohman commented 5 years ago

No sir. My code utilize the URL query from the image itself. So, no need to invent new syntax:

1. Inline Image
![Markdown Logo](/md.png?width=100&height=100 "You can still add title here")

2. Reference Image
![Markdown Logo][image]
[image]: /md.png?width=100&height=100 "You can still add title here too"

It also backward-compatible to other parsers. So, if you decided to use another markdown parser, the automatic width and height attribute created by the code snippet above will disappear and the URL query will still there. But it will not break the image because after all it just a query string. You could use URL hash as well:

[Test Image](./path/to/image.png#100x100)

And get the image dimension from:

$dimension = explode('#', $Inline['element']['attributes']['src'], 2)[1] ?? "";
if (preg_match('/\d+x\d+/', $dimension)) { ... }
MarkMessa commented 5 years ago

@tovic

1. Inline Image
![Markdown Logo](/md.png?width=100&height=100 "You can still add title here")

2. Reference Image
![Markdown Logo][image]
[image]: /md.png?width=100&height=100 "You can still add title here too"

Ok, this syntax is fine for me too. Moreover, I agree with you that there are several advantages using it instead of the one I posted in the OP.

The only thing still not clear to me is what would be your proposal for the alternative reference image? Are you considering something like:

3. Reference Image Alternative
![Markdown Logo][image#100x100]
[image]: /md.png "You can still add title here too"

 

Correct?

taufik-nurrohman commented 5 years ago

The only thing still not clear to me is what would be your proposal for the alternative reference image?

I don’t know. Your style is better IMO. It just that I feel a bit wrong in adding new syntax to the existing markdown syntax. It would be better to create another syntax as a whole, something like defining new prefix for different media:

+[foo][bar "100x100"]
-[foo][bar "100x100"]
@[foo][bar "100x100"]
$[foo][bar "100x100"]
&[foo][bar "100x100"]
%[foo][bar "100x100"]
#[foo][bar "100x100"]
~[foo][bar "100x100"]
>[foo][bar "100x100"]
=[foo][bar "100x100"]
MarkMessa commented 5 years ago

@tovic

Your style is better IMO.

Note: This is not my style. I just copy&paste other's idea.

Ok, in that case I'm gonna propose the following syntaxes:

1. Inline Image
![Markdown Logo](/md.png "100x100")

2. Reference Image
![Markdown Logo][image]
[image]: /md.png "100x100"

3. Reference Image Alternative
![Markdown Logo][image "100x100"]
[image]: /md.png

In my opinion, the main advantages of this proposal are:

Question
Any idea how to code that?

MarkMessa commented 5 years ago

@tovic

The part of the code that seems to be related with this issue is (L1434 IF statement):

if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
{
    $definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument'];
    $definition = strtolower($definition);
    $extent += strlen($matches[0]);
}
else
{
    $definition = strtolower($Element['handler']['argument']);
}

According to Regex Online Tester, ^\s*\[(.*?)\] matches expressions that begins with [] like:

[image]
     [image]

Therefore, the focus should be in trying to make this part also recognize patterns like [alt][name "w x h"].

 

So far, do you agree?

taufik-nurrohman commented 5 years ago

So you really want to rollup your language syntax then. Let me give you a clue. The first results of the regex above (the (.*?) part) is anything that match between the square bracket. So, in the code above, $matches[1] would capture name "w x h". From there you could do something like this:

list($id, $sizes) = explode(' ', $matches[1], 2); // First part is the reference ID, second part is for the sizes
$sizes = trim(trim($sizes), '\'"'); // Remove quotes

list($width, $height) = explode('x', $sizes); // Get width and height

// Then you need to put that `$id` (the original reference ID) somewhere to maps the data back to the corresponding placement. Example:
$definition = strtolower($id);
MarkMessa commented 5 years ago

@tovic

So you really want to rollup your language syntax then.

If you have another syntax proposal, please let me know. As long as is simple and consistent, for me is just fine. I could easily go on with something like:

1. Inline Image
![Markdown Logo]md.png?width=100&height=100 "You can still add title here")

2. Reference Image
![Markdown Logo][image]
[image]: /md.png?width=100&height=100 "You can still add title here too"

3. Reference Image Alternative
![Markdown Logo][image#100x100]
[image]: /md.png "You can still add title here too"

However, as you mentioned: "Your style is better IMO". Therefore, I just followed your advice.

MarkMessa commented 5 years ago

@tovic

I've just replaced the following lines:

if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
{
    $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
    $definition = strtolower($definition);

    $extent += strlen($matches[0]);
}
else
{
    $definition = strtolower($Element['text']);
}

 

With:

if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
{
    if(preg_match('/(.*?) \"\d+x\d+\"$/', $matches[1]))
    {
        $xmatches = explode(' "', $matches[1], 2);
        if(isset($xmatches[1]))
        {
            list($id, $size) = $xmatches;
            $size = trim(trim($size), '\'"');

            if (preg_match('/^\d+x\d+$/', $size))
            {                   
                list($width, $height) = explode('x', $size);                    

                if($width > 0) $Element['attributes']['width'] = $width;
                if($height > 0) $Element['attributes']['height'] = $height;
            }

            $definition = strtolower($id);

        }
    }
    else
    {
        $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
        $definition = strtolower($definition);
    }

    $extent += strlen($matches[0]);
}
else
{
    $definition = strtolower($Element['text']);
}

 

This way, the following syntaxes are now working:

![Markdown Logo][shortname "100x100"]
[shortname]: /md.png

![Markdown Logo][long image name "100x100"]
[long image name]: /md.png

 

Although the code seems to be working fine, it also seems to be overly complicated.
Let me know what you think.

taufik-nurrohman commented 5 years ago

[...] It also seems to be overly complicated.

No it isn’t. It is good. Just maybe you can remove this line as it has been checked in the second if.

if (preg_match('/^\d+x\d+$/', $size)) { ... }
bilogic commented 3 years ago

Was this implemented in https://github.com/erusev/parsedown?