# Extending LaTeX.js
To work on LaTeX.js itself and to extend it, first clone this repository.
# Architecture
The generated PEG parser parses the LaTeX code. While doing so, it calls appropriate generator functions. The generator then uses the Macros class to execute the macros that the parser encounters.
Both, the parser and the macros create the resulting HTML DOM tree by calling the HtmlGenerator functions.
The generator also holds the stack, the lengths, counters, fonts, references, etc. It provides some of TeX's primitives and basic functionality, so to speak.
# Directory Structure
General structure:
src
: all the LaTeX.js sourcesbin
: the compiled CLIdist
: the compiled and minified sourcedocs
: the webpage and playgroundwebpage
: the compiled webpage and playgroundtest
: unit tests and test driver
Files and classes needed to translate LaTeX documents to HTML documents:
the parser:
src/latex-parser.pegjs
the generator:
src/generator.ls
andsrc/html-generator.ls
macros and documentclasses:
src/latex.ltx.ls
,src/symbols.ls
,src/documentclasses/*.ls
packages:
src/packages/*.ls
the CLI:
src/latex.js.ls
the webcomponent:
src/latex.component.mjs
the library API:
src/index.mjs
Files needed to display the generated HTML document:
src/js/
(and thusdist/js/
): JavaScript that is needed by the resulting HTML documentsrc/css/
(and thusdist/js/
): CSS needed by translated HTML documentsrc/fonts/
(and thusdist/fonts
): fonts included by the translated HTML document
# Tests
To build it and run the tests, clone this repository and execute:
npm install
npm run build # or devbuild
npm test
To verify the screenshots (the CSS tests), ImageMagick
needs to be installed. Screenshots are taken
with Chromium using puppeteer
.
# Documentation and Playground
To build the website, including the playground, execute:
npm run docs
# Definition of Custom Macros
To define your own LaTeX macros in JavaScript and extend LaTeX.js, you have to create a class that contains these macros
and pass it to the HtmlGenerator
constructor in the options
object as CustomMacros
property. For instance:
var generator = new latexjs.HtmlGenerator({
CustomMacros: (function() {
var args = CustomMacros.args = {},
prototype = CustomMacros.prototype;
function CustomMacros(generator) {
this.g = generator;
}
args['bf'] = ['HV']
prototype['bf'] = function() {
this.g.setFontWeight('bf')
};
return CustomMacros;
}())
});
to define the LaTeX2.09 macro \bf
.
If you are going to define custom macros in an external file and you want to use that with the CLI, you will have to name the file just like the class, or you will have to default export it.
# Macro Arguments
CustomMacros.args
above is a <Map (opens new window)<string (opens new window), Array (opens new window)<string (opens new window)>>>, mapping the macro name to the type and arguments of
the macro. If a macro doesn't take arguments and is a horizontal-mode macro, args
can be left undefined for it.
The first array entry of args[<macro name>]
declares the macro type:
type | meaning |
---|---|
H | horizontal-mode macro |
V | vertical-mode macro - ends the current paragraph |
HV | horizontal-vertical-mode macro: must return nothing, i.e., doesn't create output |
P | only in preamble |
X | special entry, may be used multiple times; execute action (macro body) already now with whatever arguments have been parsed so far; this is needed when things should be done before the next arguments are parsed - no value should be returned by the macro in this case, for it will just be ignored |
The rest of the list (array entries) declares the arguments:
arg | delimiters | meaning | content | output |
---|---|---|---|---|
s | optional star | |||
g | { } | LaTeX code group (possibly long) | TeX allows \endgraf , but not \par ... so allow \par as well | |
hg | { } | restricted horizontal mode material | ||
o? | [ ] | optional arg | LaTeX code | |
h | restricted horizontal mode material | |||
i | { } | id | letters only | |
i? | [ ] | optional id | letters only | |
k | { } | key | anything but = and , | |
k? | [ ] | optional key | anything but = and , | |
csv | { } | comma-separated values | ||
csv? | [ ] | optional comma-separated values | ||
kv? | [ ] | optional key-value list | ||
u | { } | url | a URL as specified by RFC3986 | |
c | { } | color specification | name or float or float,float,float | |
m | { } | macro | \macro | |
l | { } | length | ||
lg? | { } | optional length group | ||
l? | [ ] | optional length | ||
cl | { } | coordinate/length | <float> or TeX length | |
cl? | [ ] | optional coordinate/length | ||
n | { } | num expression | ||
n? | [ ] | optional num expression | ||
f | { } | float expression | ||
v | ( ) | vector, a pair of coordinates | (float/length, float/length) | |
v? | optional vector | |||
is | ignore (following) spaces |
So, in the following example, the macro \title
would be a horizontal-vertical-mode macro that takes one mandatory
TeX-group argument:
args['title'] = ['HV', 'g'];
Macros with types H
or V
have to return an array.
Environments take the return value of the corresponding macro and add their content as child/children to it.
← API Limitations →