Support \includegraphics#1620
Conversation
|
This PR triggers many flow errors. I've done a good faith effort to fix them and succeeded at fixing a few. I don't intend to spend any more time on it. If someone wishes to collaborate with me on this PR and fix the flow errors, I would welcome that. |
|
@ronkok this is awesome! I'm glad to see it was too difficult. One thing that you could do to make it easier for people collaborate on this PR is push your branch to |
|
@kevinbarabash We can push directly to |
|
I feel like embedding arbitrary images is also a potential security hole, in that it could be used for tracking users. This is a third situation (I believe) where we might want some kind of In this case, we should probably also restrict the URLs to use protocols from the allowedProtocols setting. But I also fear that's not enough -- links are relatively safe, while embeds less so... |
Codecov Report
@@ Coverage Diff @@
## master #1620 +/- ##
==========================================
- Coverage 93.89% 93.67% -0.22%
==========================================
Files 79 80 +1
Lines 4571 4666 +95
Branches 796 813 +17
==========================================
+ Hits 4292 4371 +79
- Misses 247 261 +14
- Partials 32 34 +2
Continue to review full report at Codecov.
|
src/functions/includegraphics.js
Outdated
| import type {CssStyle} from "../domTree"; | ||
|
|
||
| // TODO: make it easier for commands to get the macro expanded text | ||
| // from inside an ordgroup argument. |
There was a problem hiding this comment.
@edemaine how difficult do you think this would be? The code below for stringFromParseGroup is incomplete since it doesn't handle nested groups. Also, it seems inefficient b/c we're traversing the tree when we should be able to slice part of the expanded token stream and more easily concatenate those tokens into a string.
src/functions/includegraphics.js
Outdated
| import mathMLTree from "../mathMLTree"; | ||
| import {assertNodeType, checkNodeType} from "../parseNode"; | ||
|
|
||
| const stringFromParseGroup = function(textArray: [ParseNode<"textord">]): string { |
There was a problem hiding this comment.
@ronkok [T] means a 1-tuple of type T where as T[] is an array of T.
| const style = {height: height + depth + "em"}; | ||
| const style: CssStyle = {height: height + depth + "em"}; | ||
| if (width > 0) { | ||
| style.width = width + "em"; |
There was a problem hiding this comment.
The flow errors here stemmed from the fact that when you create an object literal, flow considers that to be its type and complains about adding new properties after the fact. To get around this you have to specify the type explicitly.
src/functions/includegraphics.js
Outdated
| const width = calculateSize(group.width, options); | ||
| node.setAttribute("width", width + "em"); | ||
| } | ||
| node.setAttribute("src", group.path); |
There was a problem hiding this comment.
flow actually helped me find this error.
| |\implies|$P\implies Q$|`P\implies Q`| | ||
| |\in|$\in$|| | ||
| |\includegraphics|<span style="color:firebrick;">Not supported</span>|[Issue #898](https://github.com/Khan/KaTeX/issues/898)| | ||
| |\includegraphics|$\includegraphics[height=0.8em, totalheight=0.9em, width=0.9em, alt=KA logo]{https://cdn.kastatic.org/images/apple-touch-icon-57x57-precomposed.new.png}$|`\includegraphics[height=0.8em, totalheight=0.9em, width=0.9em, alt=KA logo]{https://cdn.kastatic.org/images/apple-touch-icon-57x57-precomposed.new.png}`| |
There was a problem hiding this comment.
I assume this also works with relative URLs as well.
There was a problem hiding this comment.
I checked this on https://deploy-preview-1620--katex.netlify.com/docs/next/support_table.html#i and it isn't rendering. I think updating this branch should fix it since #1631 was merged.
There was a problem hiding this comment.
Disregard my comment about this not working on netlify, I was looking in the wrong spot. 😅
src/functions/includegraphics.js
Outdated
| props: { | ||
| numArgs: 1, | ||
| numOptionalArgs: 1, | ||
| argTypes: ["text", "text"], |
There was a problem hiding this comment.
I forgot that we have a url arg type that you could use.
There was a problem hiding this comment.
When I first wrote this, I tried to specify a url arg type but got an error. I'll try again and see if I can identify the problem.
There was a problem hiding this comment.
@ronkok The url argument is hardcoded in the Lexer and the Parser:
https://github.com/Khan/KaTeX/blob/f71f469d4b3ca97654702a0807e736d176f4c4c5/src/Lexer.js#L49-L52
https://github.com/Khan/KaTeX/blob/9b7e10e4f753786f125254a79d7cd88acc08afd4/src/Parser.js#L916-L917
You'll need to update regexes.
There was a problem hiding this comment.
Regarding a url arg type. There really isn't any such thing in KaTeX. I've experimented a bit with href.js and replaced argTypes: ["url"] with argTypes: ["text"] and argTypes: ["chaos"]. They both run fine. It doesn't matter what arg type is specified in href.js because Parser.js ignores that specification.
What is really happening is that Lexer.js is preempting the usual logic for functions \href and \url. That urlFunctionRegexString captures both the function name and the first argument group. Parser.js then handles the result as an exception to the regular run of parsing code.
That RegEx, by the way, is not detailed enough to ensure that the argument is a valid url. It seems to be just enough to capture the function name and its first argument group.
So I question the usefulness of changing the code in this PR. The \href example lexes some arbitrary text then parses it as an exception to regular input. This PR does the same thing; it just does it in a different file. And, I would argue, by placing this exception code in the includegraphics.js file, I am grouping exeption code together in manner similar to the way we currently use defineFunction.
There was a problem hiding this comment.
@ronkok In Parser, there are escaping routines and allowed protocol checks:
https://github.com/Khan/KaTeX/blob/9b7e10e4f753786f125254a79d7cd88acc08afd4/src/Parser.js#L926-L938
There was a problem hiding this comment.
@ylemkimon That's an excellent point. I'll move that code into a utility function so that it is available to includegraphics.
If someone wants to refactor Lexer and Parser in an way that creates an actual url arg type, that would be great, but that's not for me.
There was a problem hiding this comment.
Sounds good.
argTypes: ["chaos"]. They both run fine.
That feels like a bug. @ronkok could you file an issue for this?
| if (keyVal.length === 2) { | ||
| const str = keyVal[1].trim(); | ||
| switch (keyVal[0].trim()) { | ||
| case "alt": |
There was a problem hiding this comment.
Thanks for adding this as an option.
| if (group.totalheight.number > 0) { | ||
| depth = calculateSize(group.totalheight, options) - height; | ||
| depth = Number(depth.toFixed(2)); | ||
| } |
There was a problem hiding this comment.
I see, totalheight can be used to vertically shift the image within the group.
|
|
||
| const style: CssStyle = {height: height + depth + "em"}; | ||
| if (width > 0) { | ||
| style.width = width + "em"; |
There was a problem hiding this comment.
I forgot what calculateSize did for a second and was confused why we threw away the supplied units, but then remember.
src/functions/includegraphics.js
Outdated
| if (width > 0) { | ||
| style.width = width + "em"; | ||
| // Over-rule any max-width: 100% that the img may inherit. | ||
| style.maxWidth = width + "em"; |
There was a problem hiding this comment.
Should we do the same for maxHeight? It seems less likely to be an issue.
There was a problem hiding this comment.
Or we could add a few lines to the KaTeX CSS that insulate a .katex .img from the inherited .img CSS. Something like what PR #1083 did for SVGs. Then this line of code would be unnecessary.
| return node; | ||
| }, | ||
| mathmlBuilder: (group, options) => { | ||
| const node = new mathMLTree.MathNode("mglyph", []); |
There was a problem hiding this comment.
This seems like an odd choice since it's not really a glyph. I assume this what other people are doing when they add images to MathML.
There was a problem hiding this comment.
This seems like an odd choice since it's not really a glyph.
Agreed. I used it because all the other MathML elements seemed even less suited than <mglyph>.
I assume this what other people are doing when they add images to MathML.
I haven't found any examples where people add general images to MathML. For glyphs specfically, then yes, the MathML spec does have a section. But general images? We may be breaking new ground.
| it("should produce mords", function() { | ||
| expect(getBuilt(img)[0].classes).toContain("mord"); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Can you add some failing tests to check the exceptions thrown in certain errors cases. Maybe try https://jestjs.io/docs/ro/next/expect#tothrowerrormatchinginlinesnapshot apparently it will update the source code of the test file with a string version of the exception being thrown when you run yarn jest -u.
There was a problem hiding this comment.
I'll work something up.
There was a problem hiding this comment.
@kevinbarabash In order for jest to use inline snapshots, a project must have prettier installed. [ Reference]. I'll look for another way to get some confidence in the exceptions.
There was a problem hiding this comment.
I didn't know that. In that case disregard my suggestion.
|
In the latest commit, I've changed the way that the function matches the The PR still does not do anything to address @edemaine's security concerns but I think it is otherwise ready to go. |
|
I've updated this PR to coordinate with the recently added "raw" ArgType. (Thanks!) The graphics are running again and I've eliminated all but one of the flow errors, which is better than my past performance. I still am unsure about how to address the security issue. I suspect that's a little above my pay grade. |
|
@ronkok I'll look into this next week. |
|
Looks like the netlify issue fixed itself. The only thing left an issue with the flow typing. |
|
No worries. |
| return {type: "raw", mode: "text", | ||
| string: this.parseStringGroup("raw", optional, true).text}; | ||
| case "raw": { | ||
| const token = this.parseStringGroup("raw", optional, true); |
There was a problem hiding this comment.
@ronkok the issue was that the return type for parseStringGroup is ?Token so a null check was required.
| string: token.text, | ||
| }; | ||
| } else { | ||
| throw new ParseError("Expected raw group", this.nextToken); |
There was a problem hiding this comment.
@ronkok is it possible to construct a test case where this would happen?
There was a problem hiding this comment.
@kevinbarabash Nothing comes readily to mind. I'll give it a try tomorrow.
There was a problem hiding this comment.
@kevinbarabash I can trigger this parse error by writing a Unicode combining character as the first character in the raw string. Do you want me to write that test into katex-spec.js?
There was a problem hiding this comment.
Test is added. I think I'm done here. Thanks again to @ylemkimon for adding the raw arg type.
|
@ronkok thanks for adding this feature. I'm sure people will find some really great uses for it. |
|
As I wrote above:
I think we really need some kind of |
|
Agree. We should make sure we have a |
This reverts commit f713f32.
This PR provides partial support for the
\includegraphicsfunction from thegraphixpackage. Thus it addresses issue #898 and possibly #1596.In the first argument, this PR supports the following key/value pairs:
height=<size>width=<size>totalheight=<size>alt=<string>Items 1 thru 3 in that list match some of the arguments in the
graphixpackage. The provision foraltis non-standard. Othergraphixoptions such asscaleorangleare not supported. Perhaps that could be done for a future release.Given this input:
We get the following rendering: