Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support image size instructions #1279

Closed
timmkrause opened this issue May 28, 2018 · 9 comments
Closed

Support image size instructions #1279

timmkrause opened this issue May 28, 2018 · 9 comments

Comments

@timmkrause
Copy link

timmkrause commented May 28, 2018

IMPORTANT: Unfortunately this was working in version 0.3.19 and is "broken" in 0.4.0 (I think from your perspective this is "by design" to comply with the standards...?).

Describe the feature
I'd like marked to detect

![the name](files/foo.jpg =20%x)

as an image link so that this piece of code would execute

renderer.image = function (href, title, text) {
    links.push(href);
    return marked.Renderer.prototype.image.apply(this, arguments);
};

https://github.com/tcort/markdown-link-extractor/blob/master/index.js

Is this feasible regarding the "standards move" you're going through atm?
I don't think I have to write too much about this feature, it is simply WIDTHxHEIGHT.

Why is this feature necessary?
https://github.com/tcort/markdown-link-check or to be more precise https://github.com/tcort/markdown-link-extractor is using marked to detect links in Markdown and this is currently not working with the case mentioned above.

Describe alternatives you've considered
I already tried to modify

link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,

to understand the image size instructions and this worked for my special case but broke several tests.
I also know this wouldn't suffice the need of marked because this token needs to be transformed as well.

![the name](files/foo.jpg =20%x)

->

<p>
  <img src="files/foo.jpg" alt="the name" width="20%">
</p>

=150x	  width="150"
=x100                  height="100"
=150x100  width="150"  height="100"
=20%x     width="20%"
=x10%                  height="10%"
=20%x10%  width="20%"  height="10%"
=150x10%  width="150"  height="10%"
=20%x100  width="20%"  height="100"

If the implementation does not match with your goals/roadmap can you guide me to what the easiest way would be to recognize these patterns as an image (renderer.image() is called)?

This is the regex I was using/adding: https://regex101.com/r/YF3Z3b/3/tests
Applied to the link pattern:

link: /^!?\[(label)\]\(href(?: =\d*%?x\d*)(?:\s+(title))?\s*\)/,
@styfle
Copy link
Member

styfle commented May 28, 2018

Hi @timmkrause thanks for reporting an issue!

It seems like if this was working before, it was undocumented and likely broke in the standards move (as you mentioned).

Is this "image size instruction" part of CommonMark, GFM, or other Standard?

@timmkrause
Copy link
Author

@styfle I double checked this and did not find something in any of the standards you're currently focusing on. On the other hand this is a pretty wide spread feature.

Can you or someone guide me to what I need to do to get a correct call to renderer.image() with a working href?

@UziTech
Copy link
Member

UziTech commented May 29, 2018

pretty wide spread feature.

I've never heard of this feature. What other markdown libraries support this?

@timmkrause
Copy link
Author

I don't have experience with other Markdown libraries. I can only mention some editors/platforms that do support it, just to mention a few: Mou, Marked 2, Visual Studio Code, VSTS Wiki...

I am not asking you to push this through or move away from your GFM/CommonMark goals.

I'd just like to know what the best approach would be to extend/adjust this to my needs as marked is also built for extensibility, right?

@UziTech
Copy link
Member

UziTech commented May 29, 2018

You can check out the documentation to changing the parser and renderer

It seems that syntax is very limited and the work around is to use html
https://stackoverflow.com/a/14747656/806777

<img src="files/foo.jpg" alt="the name" style="width: 20%;"/>

Duplicate of #339

@UziTech UziTech closed this as completed May 29, 2018
@UziTech
Copy link
Member

UziTech commented May 29, 2018

Here is a little script I came up with to create a wrapper around marked that does this:

// marked-with-image-size module
var marked = require("marked")

var imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;

marked.InlineLexer.rules.normal.link = imageSizeLink;
marked.InlineLexer.rules.gfm.link = imageSizeLink;
marked.InlineLexer.rules.breaks.link = imageSizeLink;

var renderer = new marked.Renderer();
renderer.image = function(href, title, text) {
  if (this.options.baseUrl && !originIndependentUrl.test(href)) {
    href = resolveUrl(this.options.baseUrl, href);
  }
  var size = href.match(/\s+=([\w%]+)?x([\w%]+)?$/);
  if (size) {
    href = href.substring(0, href.length - size[0].length);
  }
  var out = '<img src="' + href + '" alt="' + text + '"';
  if (title) {
    out += ' title="' + title + '"';
  }
  if (size) {
    out += ' style="width:' + size[1] + '; height:' + size[2] + ';"';
  }
  out += this.options.xhtml ? '/>' : '>';
  return out;
};

marked.setOptions({renderer: renderer});

module.exports = marked;

Then to use the wrapper module:

var marked = require('marked-with-image-size');

marked('![alt](src.png =20%x20% "hi")');
// outputs: <p><img src="src.png" alt="alt" title="hi" style="width:20%; height:20%;"></p>

🚨 NOT PRODUCTION CODE 🚨

To use this in production you will need to do more error checks and make sure there are no ReDoS vectors in the new regex (Which I'm pretty sure there are).

But this should give you an idea of how to update Lexer.

Hopefully we can make it easier in the future.

@timmkrause
Copy link
Author

@UziTech I really appreciate your code! Thank you so much, this will really help in markdown-link-check.

Two questions left and then I will leave this topic alone... ;-)

  1. You wrote "I came up with" - In which context did you came up with it and why?

  2. In case something breaks someday: How have you build this crazy regex? Currently it is

     link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
    

Is there some pattern to achieve the one you mentioned above?

@UziTech
Copy link
Member

UziTech commented May 29, 2018

  1. "I came up with" just means I think it should work based on the way the code is written. Why? because I wanted to see what it would be like to change the lexer from a wrapper. (Conclusion: we should make it easier.)

  2. The regexes in marked are constructed using an edit function.

    The actual regex construction is:

    ...
        link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/
    ...
    
    inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
    inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?)/;
    inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
    
    inline.link = edit(inline.link)
      .replace('label', inline._label)
      .replace('href', inline._href)
      .replace('title', inline._title)
      .getRegex();

    Which results in the regex:

    inline.link = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/

    I just added (?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)? at the end of inline._href and used the href from the lexer inside the renderer to get ahold of the image size

We are currently trying to resurrect marked and make sure it is secure and spec compliant for the >3000 dependents of marked.

Hopefully in the future we will be able to make marked easier to extend.

@FunMiles
Copy link

This thread is old but still relevant. Nevertheless, the above code does not work for me with the 4.0.8 version. However the following prologue does:

import { marked, Lexer } from 'marked';
const imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;
Lexer.rules.inline.normal.link = imageSizeLink;
Lexer.rules.inline.gfm.link = imageSizeLink;
Lexer.rules.inline.breaks.link = imageSizeLink;

Thanks to @UziTech for this workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants