Skip to content

Instantly share code, notes, and snippets.

@IvanaGyro
Last active February 26, 2023 10:56
Show Gist options
  • Save IvanaGyro/b4ed136dbfaf2330f71f8fa75475a258 to your computer and use it in GitHub Desktop.
Save IvanaGyro/b4ed136dbfaf2330f71f8fa75475a258 to your computer and use it in GitHub Desktop.
Insert math equations into the online version of Word.
name: Formulas for Word
description: Insert math equations into the online version of Word.
host: WORD
api_set: {}
script:
content: >
var editor;
const MathJax = window["MathJax"];
const ace = window["ace"];
const toastr = window["toastr"];
const Canvg = window["Canvg"];
init();
$("#insert").click(() => tryCatch(insertEquation));
subscribeSelectionChanged();
/** Default helper for invoking an action and handling errors. */
async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user through your add-in's UI.
toastr.error("", error);
console.error(error);
}
}
async function wait(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function waitUntilInternal(condition, deadline, intervalMs) {
if (Date.now() >= deadline) {
console.log("timeout");
return;
}
if (await condition()) {
return;
}
await wait(intervalMs);
return waitUntilInternal(condition, deadline, intervalMs);
}
async function waitUntil(condition, timeoutMs, intervalMs = 100) {
return waitUntilInternal(condition, Date.now() + timeoutMs, intervalMs);
}
function init() {
editor = ace.edit("editor");
editor.setTheme("ace/theme/clouds");
editor.getSession().setMode("ace/mode/latex");
editor.$blockScrolling = Infinity;
editor.getSession().setUseWrapMode(true);
// editor.setTheme("ace/theme/chrome");
editor.setOption("minLines", 10);
// editor.setOption("maxLines", Infinity);
// editor.setOption("maxLines", 15);
editor.setAutoScrollEditorIntoView(true);
editor.setShowPrintMargin(false);
// editor.setPrintMarginColumn(100);\
editor.on("change", () => {
let backup = editor.getValue();
typeset(() => {
const math = document.getElementById("equation-output");
math.innerHTML = `\$\$${backup}\$\$`;
return [math];
});
});
toastr.options.positionClass = "toast-bottom-right";
}
let promise = Promise.resolve(); // Used to hold chain of typesetting calls
function typeset(code) {
promise = promise
.then(() => MathJax.typesetPromise(code()))
.catch((err) => {
console.log(err);
});
return promise;
}
async function insertEquation() {
// Inserts an image anchored to the last paragraph.
const latex = editor.getValue().replace(/ +(?= )/g, "");
// insertionLocked = true;
const svg = document.getElementById("equation-output").getElementsByTagName("svg")[0];
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
let v = await Canvg.fromString(ctx, svg.outerHTML, { emSize: 40 });
// Start SVG rendering with animations and mouse handling.
v.start();
let img = canvas.toDataURL("image/png");
img = img.substring(img.indexOf("base64,") + 7);
// Inserts an image anchored to the last paragraph.
await Word.run(async (context) => {
const picture = context.document.body.paragraphs
.getLast()
.insertParagraph("", "After")
.insertInlinePictureFromBase64(img, "End");
picture.altTextDescription = `\$\$${latex}\$\$`;
await context.sync();
});
toastr.success("", "Successfully insert the equation!");
}
async function subscribeSelectionChanged() {
// Some old handlers may not be removed after reloading.
await Office.context.document.removeHandlerAsync(Office.EventType.DocumentSelectionChanged);
Office.context.document.addHandlerAsync(Office.EventType.DocumentSelectionChanged, loadLatex);
}
async function loadLatex(something: Office.AsyncResult<void>): Promise<void>
{
Word.run(async (context) => {
const range = context.document.getSelection();
await context.sync();
const images = range.inlinePictures;
await context.load(images);
await context.sync();
if (images.items.length !== 1) {
return;
}
const image = images.items[0];
const latex = image.altTextDescription;
if (latex.startsWith("$$") && latex.endsWith("$$")) {
editor.setValue(latex.slice(2, -2));
toastr.success("Successfully load the LaTex!");
}
});
}
language: typescript
template:
content: "<div class=\"equation-container\">\n\t<div id=\"editor\"></div>\n\t<div id=\"equation-output\"></div>\n</div>\n<div class=\"button-container\">\n\t<button id=\"insert\" class=\"ms-Button\">\n <span class=\"ms-Button-label\">Insert</span>\n </button>\n</div>\n\n<script>\n\t// To use physics and colorv2 package, braket and color have to be removed.\n\t\t// Refer to https://docs.mathjax.org/en/latest/web/components/input.html#tex-component\n\t\tMathJax = {\n\t\t\ttex: {\n\t\t\t\tpackages: {\n\t\t\t\t\t'[+]': ['physics', 'colorv2'],\n\t\t\t\t\t'[-]': ['braket', 'color']\n\t\t\t\t}\n\t\t\t}\n\t };\n</script>\n<script id=\"MathJax-script\" src=\"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg-full.js\"></script>\n<script type=\"module\">\n\timport { Canvg } from 'https://cdn.jsdelivr.net/npm/canvg/+esm';\n window.Canvg = Canvg;\n</script>"
language: html
style:
content: |
body {
display: flex;
justify-content: space-between;
flex-flow: column nowrap;
margin: 0;
height: 100vh;
gap: 8px 0px;
align-content: stretch;
}
.equation-container {
display: flex;
flex-flow: column nowrap;
align-self: stretch;
flex-grow: 1;
gap: 8px 0px;
}
.button-container {
display: flex;
flex-flow: row nowrap;
padding: 0 8px 5px 8px;
gap: 0px 8px;
}
#editor {
height: 70vh;
}
#equation-output {
flex-grow: 1;
margin: 0px 8px;
background: rgb(244, 244, 244);
display: flex;
flex-flow: column nowrap;
justify-content: center;
}
.ms-Button {
display: block;
min-width: 80px;
}
language: css
libraries: |
https://appsforoffice.microsoft.com/lib/1/hosted/office.js
@types/office-js
office-ui-fabric-js@1.4.0/dist/css/fabric.min.css
office-ui-fabric-js@1.4.0/dist/css/fabric.components.min.css
core-js@2.4.1/client/core.min.js
@types/core-js
jquery@3.1.1
@types/jquery@3.3.1
https://cdnjs.cloudflare.com/ajax/libs/ace/1.15.2/ace.js
https://cdn.jsdelivr.net/npm/ace-builds@1.15.2/css/ace.min.css
https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css
https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment