siefkenj / unified-latex

Utilities for parsing and manipulating LaTeX ASTs with the Unified.js framework

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question: How to add support of new macros

udovin opened this issue · comments

I using this library in following way:

import { parse } from "@unified-latex/unified-latex-util-parse";
import { convertToHtml } from "@unified-latex/unified-latex-to-hast";

const ast = parse(content ?? "");
const html = convertToHtml(ast);

I want to add support for custom implementation for macro:

<<Some text>> % this should be generated like &laquo;Some text&raquo;
% It looks like this is a common feature and should be added to this library.

\includegraphics{url} % this should be generated like <img src="PREFIX/url" />
% It looks like it's a custom implementation, at least for html generation.

How can I add this support? It seems I should add a macro to parse and an html generator to convertToHtml, but the current interface doesn't allow this.

Yes, it seems that convertToHtml doesn't allow custom extensions right now. Here's a possible workaround:

Using htmlLike from unified-latex-util-html-like you can create macros that will be converted to HTML tags. E.g., if you insert the node htmlLike({tag: "div", attributes: {foo: "bar"}}) it will create a valid Ast.Node that when converted to HTML will be <div foo="bar"></div>.

Parse the your code. Then use the replaceNode function from unified-latex-util-replace to replace all includegraphics with the appropriate tags. Then continue converting as normal.

If you need to add your own custom macro parsing, you can pass a list of macros if you create your own unified parser:

import { unified } from "unified";
import { unifiedLatexFromString } from "./plugin-from-string";

const options = {macros: {includegraphics: { signature: "m",  processContent: myCustomFunction}}}
const parser = unified().use([unifiedLatexFromString, options]);

It seems that includegraphics not being processed is a missing feature. A PR to add that conversion to the library would be welcome :-). It would also be nice to add a hook for the html processor itself so custom processing could be added just like custom macros can be specified.

I tried this approach, and this is working as expected:

const parser = unified().use(unifiedLatexFromString, {
    macros: {
        def: { signature: "m m" },
    },
});

type Context = {
    imageBaseUrl?: string;
};

const macros: Record<string, (node: Macro, context: Context) => any> = {
    "def": () => null,
    "includegraphics": (node: Macro, context: Context) => {
        if (!node.args) {
            return null;
        }
        const args = pgfkeysArgToObject(node.args[1]);
        let style = '';
        if (args["width"] && args["width"].length) {
            style += `width:${printRaw(args["width"][0])};`;
        }
        if (args["height"] && args["height"].length) {
            style += `height:${printRaw(args["height"][0])};`;
        }
        const imageName = printRaw(node.args[3].content);
        const imageUrl = (context.imageBaseUrl ?? "") + imageName;
        return htmlLike({ tag: "img", attributes: { src: imageUrl, style: style } });
    },
};

// External values:
//     imageBaseUrl
//     content
const ast = parser.parse(content ?? "");
const context = {
    imageBaseUrl: imageBaseUrl,
};
replaceNode(ast, (node) => {
    if (node.type !== "macro") {
        return undefined;
    }
    const macro = macros[node.content];
    if (!macro) {
        return undefined;
    }
    return macro(node, context);
});
const html = convertToHtml(ast);