Logos.java

/*
 * Copyright © 2012-2025 The CTAN Team and individual authors
 *
 * This file is distributed under the 3-clause BSD license.
 * See file LICENSE for details.
 */

package org.ctan.markup;

/**
 * This utility class contains translations for logos to HTML.
 *
 * @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
 */
public interface Logos {

    /**
     * The field <code>TEX</code> contains the <span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logo in HTML.
     */
    String TEX =
            "<span class=\"t-logo\">T<span class=\"e\">e</span>X</span>";

    /**
     * The field <code>P_TEX</code> contains the unprotected <span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logo in HTML.
     */
    String P_TEX = "T<span class=\"e\">e</span>X";

    /**
     * The field <code>TETEX</code> contains the te<span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logo in HTML.
     */
    String TETEX =
            "<span class=\"t-logo\">teT<span class=\"e\">e</span>X</span>";

    /**
     * The field <code>BIBTEX</code> contains the BibTeX logo in HTML.
     */
    String BIBTEX =
            "<span class=\"t-logo\"><span class=\"sc\">Bib</span>"
                    + "<span class=\"t\">T</span>"
                    + "<span class=\"e\">e</span>X</span>";

    /**
     * The field <code>ETEX</code> contains the eTeX logo in HTML.
     */
    String ETEX =
            "<span class=\"t-logo\"><span class=\"t-logo\">&epsilon;-" + P_TEX
                    + "</span>";

    /**
     * The field <code>EMTEX</code> contains the emTeX logo in HTML.
     */
    String EMTEX =
            "<span class=\"t-logo\">em" + P_TEX + "</span></span>";

    /**
     * The field <code>CONTEXT</code> contains the Con<span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>t
     * logo in HTML.
     */
    String CONTEXT =
            "<span class=\"t-logo\">Con" + P_TEX + "t</span>";

    /**
     * The field <code>LATEX</code> contains the
     * L<span style="font-size: 75%; margin-left: -.36em; margin-right: -.125em;
     * text-transform: uppercase; vertical-align: .45ex;">a</span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X logo in
     * HTML.
     */
    String LATEX =
            "<span class=\"t-logo\">L<span class=\"a\">a</span>" + P_TEX
                    + "</span>";

    /**
     * The field <code>P_LATEX</code> contains the unprotected
     * L<span style="font-size: 75%; margin-left: -.36em; margin-right: -.125em;
     * text-transform: uppercase; vertical-align: .45ex;">a</span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X logo in
     * HTML.
     */
    String P_LATEX = "L<span class=\"a\">a</span>" + P_TEX;

    /**
     * The field <code>_LA_TEX</code> contains the (La)<span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logo in HTML.
     */
    String _LA_TEX =
            "<span class=\"t-logo\">(L<span class=\"a\">a</span>)"
                    + "<span class=\"T\">T</span><span class=\"e\">e</span>"
                    + "X</span>";

    /**
     * The field <code>LATEX_2E_</code> contains the
     * L<span style="font-size: 75%; margin-left: -.36em; margin-right: -.125em;
     * text-transform: uppercase; vertical-align: .45ex;">a</span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X(2e)
     * logo in HTML.
     */
    String LATEX_2E_ =
            "<span class=\"t-logo\">" + P_LATEX + "(2&epsilon;)</span>";

    /**
     * The field <code>LATEX2E</code> contains the
     * L<span style="font-size: 75%; margin-left: -.36em; margin-right: -.125em;
     * text-transform: uppercase; vertical-align: .45ex;">a</span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X2e logo
     * in HTML.
     */
    String LATEX2E =
            "<span class=\"t-logo\">" + P_LATEX + "2&epsilon;</span>";

    /**
     * The field <code>METAFONT</code> contains the METAFONT logo in HTML.
     */
    String METAFONT =
            "<span class=\"mflogo\">METAFONT</span>";

    /**
     * The field <code>METAPOST</code> contains the METAPOST logo in HTML.
     */
    String METAPOST =
            "<span class=\"mflogo\">METAPOST</span>";

    /**
     * The field <code>MIKTEX</code> contains the MiK<span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logo in HTML.
     */
    String MIKTEX =
            "<span class=\"t-logo\">MiK" + P_TEX + "</span>";

    /**
     * The field <code>PICTEX</code> contains the Pic<span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logo in HTML.
     */
    String PICTEX =
            "<span>P<span class=\"e\">i</span>C<span class=\"t\">T</span>"
                    + "<span class=\"e\">e</span>X</span>";

    /**
     * The field <code>XELATEX</code> contains the XeLaTeX logo in HTML.
     */
    String XELATEX =
            "<span class=\"t-logo\">X<span class=\"E\">e</span>" + P_LATEX
                    + "</span>";

    /**
     * The field <code>XE_LA_TEX</code> contains the Xe(La)TeX logo in HTML.
     */
    String XE_LA_TEX =
            "<span class=\"t-logo\">X<span class=\"E\">e</span>"
                    + "(L<span class=\"a\">a</span>)T<span class=\"e\">e</span>"
                    + "X</span>";

    /**
     * The field <code>XETEX</code> contains the XeTeX logo in HTML.
     */
    String XETEX =
            "<span class=\"t-logo\">X<span class=\"E\">e</span>"
                    + "<span class=\"t\">T</span><span class=\"e\">e</span>X"
                    + "</span>";

    /**
     * The field <code>AMS</code> contains the AMS logo in HTML.
     */
    String AMS = "AMS";

    // "<span class=\"cal-logo\">A<span class=\"M\">M</span>S</span>";

    /**
     * The field <code>AMS</code> contains the AmS-<span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logo in HTML.
     */
    String AMS_TEX =
            "<span class=\"t-logo\">" + AMS + "-" + P_TEX + "</span>";

    /**
     * The field <code>AMS</code> contains the AmS-LaTeX logo in HTML.
     */
    String AMS_LATEX =
            "<span class=\"t-logo\">" + AMS + "-" + P_LATEX + "</span>";

    /**
     * The field <code>LyX</code> contains the LyX logo in HTML.
     */
    String LYX = "<span class=\"t-logo\">"
            + "L<span class=\"y\">y</span>X" + "</span>";

    /**
     * The method <code>lookingAt</code> provides means to translate a single
     * logo.
     *
     * @param s the string to analyse
     * @param si the index
     * @param expected the expected string
     * @param i the start index
     * @param b the target buffer
     * @param logo the logo to replace
     * @return the result
     */
    private static boolean lookingAt(String s, int si, String expected, int i,
            StringBuilder b, String logo) {

        if (i >= expected.length()) {
            b.append('\\');
            b.append(logo == null ? expected : logo);
            b.append("{}");
            return true;
        }
        if (si >= s.length() || s.charAt(si) != expected.charAt(i)) {
            return false;
        }
        return lookingAt(s, si + 1, expected, i + 1, b, logo);
    }

    /**
     * Translate <span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logos to HTML.
     *
     * @param s the string to translate
     * @return the translated string
     */
    static String tex2html(String s) {

        if (s == null || s.isEmpty()) {
            return "";
        }
        return s.replaceAll("&", "&amp;")
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;")
            .replaceAll("\\\"", "&quot;")
            .replaceAll("\\\\-", "&shy;")
            .replaceAll("\\\\ +", "&nbsp;")
            .replaceAll("\r", "")
            .replaceAll("\n *\n+", "</p><p>")
            .replaceAll("\\\\par", "</p><p>")
            .replaceAll("\\\\\\\\", "<br />")
            .replaceAll("\\\\BibTeX(\\{\\})?", BIBTEX)
            .replaceAll("\\\\ConTeXt(\\{\\})?", CONTEXT)
            .replaceAll("\\\\Pi[cC]TeX(\\{\\})?", PICTEX)
            .replaceAll("\\\\XeTeX(\\{\\})?", XETEX)
            .replaceAll("\\\\XeLaTeX(\\{\\})?", XELATEX)
            .replaceAll("\\\\TeXLaTeX(\\{\\})?", _LA_TEX)
            .replaceAll("\\\\LaTeX2?e(\\{\\})?", LATEX2E)
            .replaceAll("\\\\LaTeX(2e)", LATEX_2E_)
            .replaceAll("\\\\LaTeX(\\{\\})?", LATEX)
            .replaceAll("\\\\LyX(\\{\\})?", LYX)
            .replaceAll("\\\\MiKTeX(\\{\\})?", MIKTEX)
            .replaceAll("\\\\TeXLaTeX(\\{\\})?", _LA_TEX)
            .replaceAll("\\\\teTeX(\\{\\})?", TETEX)
            .replaceAll("\\\\eTeX(\\{\\})?", ETEX)
            .replaceAll("\\\\emTeX(\\{\\})?", EMTEX)
            .replaceAll("\\\\A[mM]STeX(\\{\\})?", AMS_TEX)
            .replaceAll("\\\\A[mM]SLaTeX(\\{\\})?", AMS_LATEX)
            .replaceAll("\\\\TeX(\\{\\})?", TEX)
            .replaceAll("\\\\Meta[Ff]ont(\\{\\})?", METAFONT)
            .replaceAll("\\\\Meta[Pp]ost(\\{\\})?", METAPOST);
    }

    /**
     * Translate <span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logos from text to HTML.
     *
     * @param s the string to translate
     * @return the translated string
     */
    static String text2html(String s) {

        return text2html(s, true);
    }

    /**
     * Translate <span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logos from text to HTML.
     *
     * @param s the string to translate
     * @param translateEntities also translate HTML entities
     *
     * @return the translated string
     */
    static String text2html(String s, boolean translateEntities) {

        if (s == null || s.isEmpty()) {
            return "";
        }
        s = s.replaceAll("==>", "⇒").replaceAll("-->", "→");
        if (translateEntities) {
            s = s.replaceAll("&", "&amp;")
                .replaceAll("<", "&lt;")
                .replaceAll(">", "&gt;")
                .replaceAll("\"", "&quot;");
        }
        return s.replaceAll("([0-9])(th)([ .,;!?:])", "$1<sup>$2</sup>$3")
            .replaceAll("(1)(st)([ .,;!?:])", "$1<sup>$2</sup>$3")
            .replaceAll("(2)(nd)([ .,;!?:])", "$1<sup>$2</sup>$3")
            .replaceAll("(3)(rd)([ .,;!?:])", "$1<sup>$2</sup>$3")
            .replaceAll("\r", "")
            .replaceAll(" -- ", " &ndash; ")
            .replaceAll("---", "&mdash;")
            .replaceAll("\n *\n+", "<br />")
            .replaceAll("A[mM]S-?TeX", AMS_TEX)
            .replaceAll("A[mM]S-?LaTeX", AMS_LATEX)
            .replaceAll("AMS", AMS)
            .replaceAll("BibTeX", BIBTEX)
            .replaceAll("ConTeXt", CONTEXT)
            .replaceAll("emTeX", EMTEX)
            .replaceAll("Pi[cC]TeX", PICTEX)
            .replaceAll("XeTeX", XETEX)
            .replaceAll("XeLaTeX", XELATEX)
            .replaceAll("Xe\\(La\\)TeX", XE_LA_TEX)
            .replaceAll("eTeX", ETEX)
            .replaceAll("LaTeX2e", LATEX2E)
            .replaceAll("LaTeX(2e)", LATEX_2E_)
            .replaceAll("LaTeXe", LATEX2E)
            .replaceAll("LaTeX", LATEX)
            .replaceAll("LyX", LYX)
            .replaceAll("\\(La\\)TeX", _LA_TEX)
            .replaceAll("Meta[Ff]ont", METAFONT)
            .replaceAll("Meta[Pp]ost", METAPOST)
            .replaceAll("Mi[kK]TeX", MIKTEX)
            .replaceAll("teTeX", TETEX)
            .replaceAll("TeXLaTeX", _LA_TEX)
            .replaceAll("TeX", TEX);
    }

    /**
     * Translate <span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X</span>
     * logos from text to
     * L<span style="font-size: 75%; margin-left: -.36em; margin-right: -.125em;
     * text-transform: uppercase; vertical-align: .45ex;">a</span>T<span style=
     * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
     * margin-left:-0.2em;margin-right:-0.1em;line-height:0;" >e</span>X.
     *
     * @param s the string to translate
     * @return the translated string
     */
    static String text2latex(String s) {

        if (s == null || s.isEmpty()) {
            return "";
        }
        var out = new StringBuilder();
        char c;
        for (var i = 0; i < s.length(); i++) {
            switch (c = s.charAt(i)) {
                case '\\':
                    out.append(c);
                    if (++i >= s.length()) {
                        return out.toString();
                    }
                    c = s.charAt(i);
                    if (Character.isLetter(c)) {
                        while (++i < s.length() && Character.isLetter(c)) {
                            out.append(c);
                            c = s.charAt(i);
                        }
                        i--;
                    }
                    break;
                case 'e':
                    if (lookingAt(s, i, "eTeX", 0, out, null)) {
                        i += 3;
                        continue;
                    } else if (lookingAt(s, i, "e-TeX", 0, out, "eTeX")) {
                        i += 4;
                        continue;
                    } else if (lookingAt(s, i, "emTeX", 0, out, null)) {
                        i += 4;
                        continue;
                    }
                    break;
                case '(':
                    if (lookingAt(s, i, "(La)TeX", 0, out, "TeXLaTeX")) {
                        i += 6;
                        continue;
                    }
                    break;
                case 'A':
                    if (lookingAt(s, i, "AmSTeX", 0, out, null)
                            || lookingAt(s, i, "AmsTeX", 0, out, "AmSTeX")) {
                        i += 5;
                        continue;
                    } else if (lookingAt(s, i, "AmSLaTeX", 0, out, null)
                            || lookingAt(s, i, "AmsLaTeX", 0, out,
                                "AmSLaTeX")) {
                        i += 7;
                        continue;
                    }
                    break;
                case 'B':
                    if (lookingAt(s, i, "BibTeX", 0, out, null)) {
                        i += 5;
                        continue;
                    }
                    break;
                case 'C':
                    if (lookingAt(s, i, "ConTeXt", 0, out, null)) {
                        i += 6;
                        continue;
                    }
                    break;
                case 'T':
                    if (lookingAt(s, i, "TeX", 0, out, null)) {
                        i += 2;
                        continue;
                    }
                    break;
                case 'L':
                    if (lookingAt(s, i, "LaTeXe", 0, out, "LaTeXe")) {
                        i += 5;
                        continue;
                    } else if (lookingAt(s, i, "LaTeX2e", 0, out, "LaTeXe")) {
                        i += 6;
                        continue;
                    } else if (lookingAt(s, i, "LaTeX(2e)", 0, out,
                        "LaTeX(2e)")) {
                        i += 9;
                        continue;
                    } else if (lookingAt(s, i, "LaTeX", 0, out, null)) {
                        i += 4;
                        continue;
                    } else if (lookingAt(s, i, "LyX", 0, out, null)) {
                        i += 2;
                        continue;
                    }
                    break;
                case 'M':
                    if (lookingAt(s, i, "Metafont", 0, out, "METAFONT")
                            || lookingAt(s, i, "MetaFont", 0, out, "METAFONT")
                            || lookingAt(s, i, "Metapost", 0, out, "METAPOST")
                            || lookingAt(s, i, "MetaPost", 0, out,
                                "METAPOST")) {
                        i += 7;
                        continue;
                    } else if (lookingAt(s, i, "MiKTeX", 0, out, null)) {
                        i += 5;
                        continue;
                    }
                    break;
                case 'N':
                    if (lookingAt(s, i, "NTS", 0, out, null)) {
                        i += 2;
                        continue;
                    }
                    break;
                case 'P':
                    if (lookingAt(s, i, "PiCTeX", 0, out, null)
                            || lookingAt(s, i, "PicTeX", 0, out, "PiCTeX")) {
                        i += 5;
                        continue;
                    }
                    break;
                case 'X':
                    if (lookingAt(s, i, "XeTeX", 0, out, null)) {
                        i += 4;
                        continue;
                    } else if (lookingAt(s, i, "XeLaTeX", 0, out, null)) {
                        i += 6;
                        continue;
                    }
                    break;
                default:
                    // nothing to do
            }
            out.append(c);
        }

        return out.toString();
    }

}