{"action":"created","comment":{"url":"https://api.github.com/repos/Khan/KaTeX/pulls/comments/33898897","id":33898897,"diff_hunk":"@@ -0,0 +1,244 @@\n+\"use strict\";\n+\n+var querystring = require(\"querystring\");\n+var child_process = require(\"child_process\");\n+var fs = require(\"fs\");\n+var path = require(\"path\");\n+var Q = require(\"q\"); // To debug, pass Q_DEBUG=1 in the environment\n+var pngparse = require(\"pngparse\");\n+var fft = require(\"ndarray-fft\");\n+var ndarray = require(\"ndarray-fft/node_modules/ndarray\");\n+\n+var data = require(\"../../test/screenshotter/ss_data\");\n+\n+// ignore some tests, since they contain commands not supported by LaTeX\n+var blacklist = {\n+    Colors: \"Color handling differs\",\n+    DeepFontSizing: \"\\\\Huge inside \\\\dfrac doesn't work for some reason\",\n+    KaTeX: \"Custom command, doesn't exist in LaTeX\"\n+};\n+var todo;\n+if (process.argv.length > 2) {\n+    todo = process.argv.slice(2);\n+} else {\n+    todo = Object.keys(data).filter(function(key) {\n+        return !blacklist[key];\n+    });\n+}\n+\n+// Dimensions used when we do the FFT-based alignment computation\n+var alignWidth = 2048; // should be at least twice the width resp. height\n+var alignHeight = 2048; // of the screenshots, and a power of two.\n+\n+// Compute required resolution to match test.html. 16px default font,\n+// scaled to 4em in test.html, and to 1.21em in katex.css. Corresponding\n+// LaTeX font size is 10pt. There are 72.27pt per inch.\n+var pxPerEm = 16 * 4 * 1.21;\n+var pxPerPt = pxPerEm / 10;\n+var dpi = pxPerPt * 72.27;\n+\n+var tmpDir = \"/tmp/texcmp\";\n+var ssDir = path.normalize(\n+    path.join(__dirname, \"..\", \"..\", \"test\", \"screenshotter\"));\n+var imagesDir = path.join(ssDir, \"images\");\n+var teximgDir = path.join(ssDir, \"tex\");\n+var diffDir = path.join(ssDir, \"diff\");\n+var template;\n+\n+Q.all([\n+    Q.nfcall(fs.readFile, path.join(ssDir, \"test.tex\"), \"utf-8\"),\n+    mkdir(tmpDir),\n+    mkdir(teximgDir),\n+    mkdir(diffDir)\n+]).spread(function(data) {\n+    template = data;\n+    // dirs have been created, template has been read, now rasterize.\n+    return Q.all(todo.map(processTestCase));\n+}).done();\n+\n+// Process a single test case: rasterize, then create diff\n+function processTestCase(key) {\n+    if (blacklist[key]) return;\n+    var url = data[key];\n+    var query = url.replace(/^.*?\\?/, \"\"); // extract query string\n+    query = query.replace(/\\+/g, \"%2B\"); // plus doesn't mean space here\n+    query = querystring.parse(query);\n+    var tex = \"$\" + query.m + \"$\";\n+    if (query.display) {\n+        tex = \"$$\" + query.m + \"$$\";\n+    }\n+    if (query.pre) {\n+        tex = query.pre.replace(\"<br>\", \"\\\\\\\\\") + tex;\n+    }\n+    if (query.post) {\n+        tex = tex + query.post.replace(\"<br>\", \"\\\\\\\\\");\n+    }\n+    tex = template.replace(/\\$.*\\$/, tex.replace(/\\$/g, \"$$$$\"));\n+    var texFile = path.join(tmpDir, key + \".tex\");\n+    var pdfFile = path.join(tmpDir, key + \".pdf\");\n+    var pngFile = path.join(teximgDir, key + \"-pdflatex.png\");\n+    var browserFile = path.join(imagesDir, key + \"-firefox.png\");\n+    var diffFile = path.join(diffDir, key + \".png\");\n+\n+    // Step 1: write key.tex file\n+    var fftLatex = Q.nfcall(fs.writeFile, texFile, tex).then(function() {\n+        // Step 2: call \"pdflatex key\" to create key.pdf\n+        return execFile(\"pdflatex\", [\n+            \"-interaction\", \"nonstopmode\", key\n+        ], {cwd: tmpDir});\n+    }).then(function() {\n+        console.log(\"Typeset \" + key);\n+        // Step 3: call \"convert ... key.pdf key.png\" to create key.png\n+        return execFile(\"convert\", [\n+            \"-density\", dpi, \"-units\", \"PixelsPerInch\", \"-flatten\",\n+            pdfFile, pngFile\n+        ]);\n+    }).then(function() {\n+        console.log(\"Rasterized \" + key);\n+        // Step 4: apply FFT to that\n+        return fftPNG(pngFile);\n+    });\n+    // Step 5: apply FFT to reference image as well\n+    var fftBrowser = fftPNG(browserFile);\n+\n+    return Q.all([fftBrowser, fftLatex]).spread(function(browser, latex) {\n+        // Now we have the FFT result from both \n+        // Step 6: find alignment which maximizes overlap.\n+        // This uses a FFT-based correlation comoputation.\n+        var x, y;\n+        var real = newMatrix();\n+        var imag = newMatrix();\n+\n+        // Step 6a: (real + i*imag) = latex * conjugate(browser)\n+        for (y = 0; y < alignHeight; ++y) {\n+            for (x = 0; x < alignWidth; ++x) {\n+                var br = browser.real.get(y, x);\n+                var bi = browser.imag.get(y, x);\n+                var lr = latex.real.get(y, x);\n+                var li = latex.imag.get(y, x);\n+                real.set(y, x, br * lr + bi * li);\n+                imag.set(y, x, br * li - bi * lr);\n+            }\n+        }\n+\n+        // Step 6b: (real + i*imag) = inverseFFT(real + i*imag)\n+        fft(-1, real, imag);\n+\n+        // Step 6c: find position where the (squared) absolute value is maximal\n+        var ox = 0;\n+        var oy = 0;\n+        var ov = -1;\n+        for (y = 0; y < alignHeight; ++y) {\n+            for (x = 0; x < alignWidth; ++x) {\n+                var or = real.get(y, x);\n+                var oi = imag.get(y, x);\n+                var sq = or * or + oi * oi;\n+                if (ov < sq) {\n+                    ov = sq;\n+                    ox = x;\n+                    oy = y;\n+                }\n+            }\n+        }\n+\n+        // Step 6d: Treat negative offsets in a non-cyclic way\n+        if (oy > (alignHeight >>> 1)) {\n+            oy -= alignHeight;\n+        }\n+        if (ox > (alignWidth >>> 1)) {\n+            ox -= alignWidth;\n+        }\n+        console.log(\"Positioned \" + key + \": \" + ox + \", \" + oy);\n+\n+        // Step 7: use these offsets to compute difference illustration\n+        var uw = Math.max(browser.width, latex.width) + Math.abs(ox);\n+        var uh = Math.max(browser.height, latex.height) + Math.abs(oy);\n+        var bx = Math.max(ox, 0);\n+        var by = Math.max(oy, 0);\n+        var lx = Math.max(-ox, 0);\n+        var ly = Math.max(-oy, 0);\n+        return execFile(\"convert\", [\n+            \"(\", pngFile, \"-grayscale\", \"Rec709Luminance\",\n+            \"-extent\", uw + \"x\" + uh + \"-\" + lx + \"-\" + ly,\n+            \")\",\n+            \"(\", browserFile, \"-grayscale\", \"Rec709Luminance\",\n+            \"-extent\", uw + \"x\" + uh + \"-\" + bx + \"-\" + by,\n+            \")\",\n+            \"(\", \"-clone\", \"0-1\", \"-compose\", \"darken\", \"-composite\", \")\",\n+            \"-channel\", \"RGB\", \"-combine\", \"-trim\", diffFile","path":"dockers/texcmp/texcmp.js","position":168,"original_position":168,"commit_id":"c44148e73bf7a7ef0a0746da0802d352b68a2bbf","original_commit_id":"c44148e73bf7a7ef0a0746da0802d352b68a2bbf","user":{"login":"kevinb7","id":1044413,"avatar_url":"https://avatars.githubusercontent.com/u/1044413?v=3","gravatar_id":"","url":"https://api.github.com/users/kevinb7","html_url":"https://github.com/kevinb7","followers_url":"https://api.github.com/users/kevinb7/followers","following_url":"https://api.github.com/users/kevinb7/following{/other_user}","gists_url":"https://api.github.com/users/kevinb7/gists{/gist_id}","starred_url":"https://api.github.com/users/kevinb7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kevinb7/subscriptions","organizations_url":"https://api.github.com/users/kevinb7/orgs","repos_url":"https://api.github.com/users/kevinb7/repos","events_url":"https://api.github.com/users/kevinb7/events{/privacy}","received_events_url":"https://api.github.com/users/kevinb7/received_events","type":"User","site_admin":false},"body":"This was really helpful, can you add it as a comment?","created_at":"2015-07-05T21:09:26Z","updated_at":"2015-07-05T21:09:26Z","html_url":"https://github.com/Khan/KaTeX/pull/268#discussion_r33898897","pull_request_url":"https://api.github.com/repos/Khan/KaTeX/pulls/268","_links":{"self":{"href":"https://api.github.com/repos/Khan/KaTeX/pulls/comments/33898897"},"html":{"href":"https://github.com/Khan/KaTeX/pull/268#discussion_r33898897"},"pull_request":{"href":"https://api.github.com/repos/Khan/KaTeX/pulls/268"}}},"pull_request":{"url":"https://api.github.com/repos/Khan/KaTeX/pulls/268","id":38521240,"html_url":"https://github.com/Khan/KaTeX/pull/268","diff_url":"https://github.com/Khan/KaTeX/pull/268.diff","patch_url":"https://github.com/Khan/KaTeX/pull/268.patch","issue_url":"https://api.github.com/repos/Khan/KaTeX/issues/268","number":268,"state":"open","locked":false,"title":"Generate reference images from LaTeX","user":{"login":"gagern","id":190759,"avatar_url":"https://avatars.githubusercontent.com/u/190759?v=3","gravatar_id":"","url":"https://api.github.com/users/gagern","html_url":"https://github.com/gagern","followers_url":"https://api.github.com/users/gagern/followers","following_url":"https://api.github.com/users/gagern/following{/other_user}","gists_url":"https://api.github.com/users/gagern/gists{/gist_id}","starred_url":"https://api.github.com/users/gagern/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gagern/subscriptions","organizations_url":"https://api.github.com/users/gagern/orgs","repos_url":"https://api.github.com/users/gagern/repos","events_url":"https://api.github.com/users/gagern/events{/privacy}","received_events_url":"https://api.github.com/users/gagern/received_events","type":"User","site_admin":false},"body":"In https://github.com/Khan/KaTeX/pull/156#issuecomment-114572831, @kevinb7 wrote that he'd like to see TeX renderings of our examples before proceeding. Here they are. The docker may still be improved upon, I guess, in terms of size and similar, but it works. I also added a small tool to compute visual diffs, but haven't committed the results of this. One thing I notice is that in TeX, `\\llap` and `\\rlap` apparently take an argument in text mode.","created_at":"2015-06-25T08:01:30Z","updated_at":"2015-07-05T21:09:26Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a1b9c39a114b3e7f82c6405e5493634d43dea6c1","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/Khan/KaTeX/pulls/268/commits","review_comments_url":"https://api.github.com/repos/Khan/KaTeX/pulls/268/comments","review_comment_url":"https://api.github.com/repos/Khan/KaTeX/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Khan/KaTeX/issues/268/comments","statuses_url":"https://api.github.com/repos/Khan/KaTeX/statuses/c44148e73bf7a7ef0a0746da0802d352b68a2bbf","head":{"label":"gagern:texcmp","ref":"texcmp","sha":"c44148e73bf7a7ef0a0746da0802d352b68a2bbf","user":{"login":"gagern","id":190759,"avatar_url":"https://avatars.githubusercontent.com/u/190759?v=3","gravatar_id":"","url":"https://api.github.com/users/gagern","html_url":"https://github.com/gagern","followers_url":"https://api.github.com/users/gagern/followers","following_url":"https://api.github.com/users/gagern/following{/other_user}","gists_url":"https://api.github.com/users/gagern/gists{/gist_id}","starred_url":"https://api.github.com/users/gagern/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gagern/subscriptions","organizations_url":"https://api.github.com/users/gagern/orgs","repos_url":"https://api.github.com/users/gagern/repos","events_url":"https://api.github.com/users/gagern/events{/privacy}","received_events_url":"https://api.github.com/users/gagern/received_events","type":"User","site_admin":false},"repo":{"id":37335285,"name":"KaTeX","full_name":"gagern/KaTeX","owner":{"login":"gagern","id":190759,"avatar_url":"https://avatars.githubusercontent.com/u/190759?v=3","gravatar_id":"","url":"https://api.github.com/users/gagern","html_url":"https://github.com/gagern","followers_url":"https://api.github.com/users/gagern/followers","following_url":"https://api.github.com/users/gagern/following{/other_user}","gists_url":"https://api.github.com/users/gagern/gists{/gist_id}","starred_url":"https://api.github.com/users/gagern/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gagern/subscriptions","organizations_url":"https://api.github.com/users/gagern/orgs","repos_url":"https://api.github.com/users/gagern/repos","events_url":"https://api.github.com/users/gagern/events{/privacy}","received_events_url":"https://api.github.com/users/gagern/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gagern/KaTeX","description":"Fast math typesetting for the web.","fork":true,"url":"https://api.github.com/repos/gagern/KaTeX","forks_url":"https://api.github.com/repos/gagern/KaTeX/forks","keys_url":"https://api.github.com/repos/gagern/KaTeX/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gagern/KaTeX/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gagern/KaTeX/teams","hooks_url":"https://api.github.com/repos/gagern/KaTeX/hooks","issue_events_url":"https://api.github.com/repos/gagern/KaTeX/issues/events{/number}","events_url":"https://api.github.com/repos/gagern/KaTeX/events","assignees_url":"https://api.github.com/repos/gagern/KaTeX/assignees{/user}","branches_url":"https://api.github.com/repos/gagern/KaTeX/branches{/branch}","tags_url":"https://api.github.com/repos/gagern/KaTeX/tags","blobs_url":"https://api.github.com/repos/gagern/KaTeX/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gagern/KaTeX/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gagern/KaTeX/git/refs{/sha}","trees_url":"https://api.github.com/repos/gagern/KaTeX/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gagern/KaTeX/statuses/{sha}","languages_url":"https://api.github.com/repos/gagern/KaTeX/languages","stargazers_url":"https://api.github.com/repos/gagern/KaTeX/stargazers","contributors_url":"https://api.github.com/repos/gagern/KaTeX/contributors","subscribers_url":"https://api.github.com/repos/gagern/KaTeX/subscribers","subscription_url":"https://api.github.com/repos/gagern/KaTeX/subscription","commits_url":"https://api.github.com/repos/gagern/KaTeX/commits{/sha}","git_commits_url":"https://api.github.com/repos/gagern/KaTeX/git/commits{/sha}","comments_url":"https://api.github.com/repos/gagern/KaTeX/comments{/number}","issue_comment_url":"https://api.github.com/repos/gagern/KaTeX/issues/comments{/number}","contents_url":"https://api.github.com/repos/gagern/KaTeX/contents/{+path}","compare_url":"https://api.github.com/repos/gagern/KaTeX/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gagern/KaTeX/merges","archive_url":"https://api.github.com/repos/gagern/KaTeX/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gagern/KaTeX/downloads","issues_url":"https://api.github.com/repos/gagern/KaTeX/issues{/number}","pulls_url":"https://api.github.com/repos/gagern/KaTeX/pulls{/number}","milestones_url":"https://api.github.com/repos/gagern/KaTeX/milestones{/number}","notifications_url":"https://api.github.com/repos/gagern/KaTeX/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gagern/KaTeX/labels{/name}","releases_url":"https://api.github.com/repos/gagern/KaTeX/releases{/id}","created_at":"2015-06-12T17:29:51Z","updated_at":"2015-06-12T17:29:52Z","pushed_at":"2015-07-03T09:16:00Z","git_url":"git://github.com/gagern/KaTeX.git","ssh_url":"git@github.com:gagern/KaTeX.git","clone_url":"https://github.com/gagern/KaTeX.git","svn_url":"https://github.com/gagern/KaTeX","homepage":"http://khan.github.io/KaTeX/","size":10747,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Khan:master","ref":"master","sha":"7f1b53cbfdd1df15ee413e841e4f862dde99ae3f","user":{"login":"Khan","id":15455,"avatar_url":"https://avatars.githubusercontent.com/u/15455?v=3","gravatar_id":"","url":"https://api.github.com/users/Khan","html_url":"https://github.com/Khan","followers_url":"https://api.github.com/users/Khan/followers","following_url":"https://api.github.com/users/Khan/following{/other_user}","gists_url":"https://api.github.com/users/Khan/gists{/gist_id}","starred_url":"https://api.github.com/users/Khan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Khan/subscriptions","organizations_url":"https://api.github.com/users/Khan/orgs","repos_url":"https://api.github.com/users/Khan/repos","events_url":"https://api.github.com/users/Khan/events{/privacy}","received_events_url":"https://api.github.com/users/Khan/received_events","type":"Organization","site_admin":false},"repo":{"id":11195700,"name":"KaTeX","full_name":"Khan/KaTeX","owner":{"login":"Khan","id":15455,"avatar_url":"https://avatars.githubusercontent.com/u/15455?v=3","gravatar_id":"","url":"https://api.github.com/users/Khan","html_url":"https://github.com/Khan","followers_url":"https://api.github.com/users/Khan/followers","following_url":"https://api.github.com/users/Khan/following{/other_user}","gists_url":"https://api.github.com/users/Khan/gists{/gist_id}","starred_url":"https://api.github.com/users/Khan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Khan/subscriptions","organizations_url":"https://api.github.com/users/Khan/orgs","repos_url":"https://api.github.com/users/Khan/repos","events_url":"https://api.github.com/users/Khan/events{/privacy}","received_events_url":"https://api.github.com/users/Khan/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Khan/KaTeX","description":"Fast math typesetting for the web.","fork":false,"url":"https://api.github.com/repos/Khan/KaTeX","forks_url":"https://api.github.com/repos/Khan/KaTeX/forks","keys_url":"https://api.github.com/repos/Khan/KaTeX/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Khan/KaTeX/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Khan/KaTeX/teams","hooks_url":"https://api.github.com/repos/Khan/KaTeX/hooks","issue_events_url":"https://api.github.com/repos/Khan/KaTeX/issues/events{/number}","events_url":"https://api.github.com/repos/Khan/KaTeX/events","assignees_url":"https://api.github.com/repos/Khan/KaTeX/assignees{/user}","branches_url":"https://api.github.com/repos/Khan/KaTeX/branches{/branch}","tags_url":"https://api.github.com/repos/Khan/KaTeX/tags","blobs_url":"https://api.github.com/repos/Khan/KaTeX/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Khan/KaTeX/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Khan/KaTeX/git/refs{/sha}","trees_url":"https://api.github.com/repos/Khan/KaTeX/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Khan/KaTeX/statuses/{sha}","languages_url":"https://api.github.com/repos/Khan/KaTeX/languages","stargazers_url":"https://api.github.com/repos/Khan/KaTeX/stargazers","contributors_url":"https://api.github.com/repos/Khan/KaTeX/contributors","subscribers_url":"https://api.github.com/repos/Khan/KaTeX/subscribers","subscription_url":"https://api.github.com/repos/Khan/KaTeX/subscription","commits_url":"https://api.github.com/repos/Khan/KaTeX/commits{/sha}","git_commits_url":"https://api.github.com/repos/Khan/KaTeX/git/commits{/sha}","comments_url":"https://api.github.com/repos/Khan/KaTeX/comments{/number}","issue_comment_url":"https://api.github.com/repos/Khan/KaTeX/issues/comments{/number}","contents_url":"https://api.github.com/repos/Khan/KaTeX/contents/{+path}","compare_url":"https://api.github.com/repos/Khan/KaTeX/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Khan/KaTeX/merges","archive_url":"https://api.github.com/repos/Khan/KaTeX/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Khan/KaTeX/downloads","issues_url":"https://api.github.com/repos/Khan/KaTeX/issues{/number}","pulls_url":"https://api.github.com/repos/Khan/KaTeX/pulls{/number}","milestones_url":"https://api.github.com/repos/Khan/KaTeX/milestones{/number}","notifications_url":"https://api.github.com/repos/Khan/KaTeX/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Khan/KaTeX/labels{/name}","releases_url":"https://api.github.com/repos/Khan/KaTeX/releases{/id}","created_at":"2013-07-05T09:01:54Z","updated_at":"2015-07-05T04:26:07Z","pushed_at":"2015-07-04T21:36:55Z","git_url":"git://github.com/Khan/KaTeX.git","ssh_url":"git@github.com:Khan/KaTeX.git","clone_url":"https://github.com/Khan/KaTeX.git","svn_url":"https://github.com/Khan/KaTeX","homepage":"http://khan.github.io/KaTeX/","size":12550,"stargazers_count":3709,"watchers_count":3709,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":199,"mirror_url":null,"open_issues_count":88,"forks":199,"open_issues":88,"watchers":3709,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Khan/KaTeX/pulls/268"},"html":{"href":"https://github.com/Khan/KaTeX/pull/268"},"issue":{"href":"https://api.github.com/repos/Khan/KaTeX/issues/268"},"comments":{"href":"https://api.github.com/repos/Khan/KaTeX/issues/268/comments"},"review_comments":{"href":"https://api.github.com/repos/Khan/KaTeX/pulls/268/comments"},"review_comment":{"href":"https://api.github.com/repos/Khan/KaTeX/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Khan/KaTeX/pulls/268/commits"},"statuses":{"href":"https://api.github.com/repos/Khan/KaTeX/statuses/c44148e73bf7a7ef0a0746da0802d352b68a2bbf"}}}}