Wiki source code of Template Mechanism
Last modified by Vincent Massol on 2026/08/30 19:36
Show last authors
| author | version | line-number | content |
|---|---|---|---|
| 1 | The renderer carries no LaTeX of its own in Java. Exporting a page walks its XDOM, the tree of Blocks the wiki syntax was parsed into, and renders each Block with a Velocity template holding the LaTeX commands to produce. Which template that is is decided by name, and the name comes from the Block. | ||
| 2 | |||
| 3 | {{plantuml}} | ||
| 4 | @startuml | ||
| 5 | !theme bluegray | ||
| 6 | skinparam componentStyle rectangle | ||
| 7 | |||
| 8 | component "**XDOM**\n//the parsed page, one Block per piece of content//" as XDOM | ||
| 9 | component "**Template name**\n//the Block's class name,//\n//or its latex-template parameter//" as NAME | ||
| 10 | component "**Your template**\n//searched first//" as MINE | ||
| 11 | component "**The shipped default**\n//searched next//" as DEF | ||
| 12 | component "**The Block's children**\n//processed in its place//" as KIDS | ||
| 13 | component "**LaTeX**" as TEX | ||
| 14 | |||
| 15 | XDOM --> NAME : for each Block | ||
| 16 | NAME --> MINE | ||
| 17 | MINE --> DEF : not found | ||
| 18 | DEF --> KIDS : not found | ||
| 19 | MINE --> TEX : rendered | ||
| 20 | DEF --> TEX : rendered | ||
| 21 | @enduml | ||
| 22 | {{/plantuml}} | ||
| 23 | |||
| 24 | == One Template per Block == | ||
| 25 | |||
| 26 | A Block asks for the template bearing its own simple class name: a ##WordBlock## is rendered by ##WordBlock##, a ##ParagraphBlock## by ##ParagraphBlock##. The XDOM sitting at the root of the tree is a Block like the others, so it has a template too, ##XDOM##, and that one stands for the whole document. | ||
| 27 | |||
| 28 | The templates the extension ships live inside its jar, as classloader resources under ##templates/latex/default/##. Reading the one named after a Block is the shortest way to find out what that Block currently produces. | ||
| 29 | |||
| 30 | A Block whose template is found nowhere is not dropped: its children are processed in its place, and only the container's own LaTeX is lost. | ||
| 31 | |||
| 32 | == Where a Template Is Looked Up == | ||
| 33 | |||
| 34 | Every Block resolves to **two** paths, tried in that order: ##latex/<name>##, which is yours, then ##latex/default/<name>##, which is the extension's. The first that exists wins, so a ##latex/ParagraphBlock## you supply replaces the shipped ##latex/default/ParagraphBlock## for every paragraph of every export. | ||
| 35 | |||
| 36 | Each of the two paths is then resolved by XWiki's own template lookup, which searches three places in order: | ||
| 37 | |||
| 38 | * **The current skin.** When the skin is a wiki page, an ##XWiki.XWikiSkinFileOverrideClass## xobject whose ##path## is that path, which is how you [[override a template>>doc:documentation.extensions.dev.latex.override-template.WebHome]], or an xproperty of its ##XWiki.XWikiSkins## xobject named after the path — see the [[Skin Application>>doc:extensions:Extension.Skin Application]]. When the skin is on the filesystem, a file at that path under ##skins/<skin name>/##. | ||
| 39 | * **The ##templates## directory of the webapp**, so ##templates/latex/<name>## for the first path. | ||
| 40 | * **The classloader**, again under ##templates/##. This is where the shipped templates are found inside the extension's jar, and it is what makes ##WEB-INF/classes/templates/latex/<name>## an override point that needs no skin at all. | ||
| 41 | |||
| 42 | == The Document Template == | ||
| 43 | |||
| 44 | The ##XDOM## template is the frame everything else is rendered into: | ||
| 45 | |||
| 46 | {{code language="tex"}} | ||
| 47 | \documentclass{$latex.properties.documentClass} | ||
| 48 | |||
| 49 | $latex.processor.render('Preamble') | ||
| 50 | |||
| 51 | \begin{document}$latex.processor.process($latex.block.getChildren()) | ||
| 52 | |||
| 53 | \end{document} | ||
| 54 | {{/code}} | ||
| 55 | |||
| 56 | Two things follow from it. ##Preamble## is a template belonging to no Block at all: it exists only because the ##XDOM## template renders it by name, which is what makes ##latex/Preamble## the place to change everything between the document class and ##\begin{document}##. And the class is not fixed — it comes from the export's [[##documentClass## option>>doc:documentation.extensions.user.latex.export-page.export-options.WebHome]], falling back to ##article## when that option is left empty. | ||
| 57 | |||
| 58 | The shipped template wraps these lines in the ##XDOM## [[extension points>>doc:documentation.extensions.dev.latex.extension-points.WebHome]], which is how an extension adds to the generated document without taking the template over. | ||
| 59 | |||
| 60 | == Naming a Template on a Block == | ||
| 61 | |||
| 62 | A Block can also ask for a template by name rather than by class, through a ##latex-template## parameter. Here one table cell is rendered by the ##custom1## template: | ||
| 63 | |||
| 64 | {{code language="none"}} | ||
| 65 | |=Head1|=Head2 | ||
| 66 | |cell1|(% latex-template="custom/custom1" %)cell2 | ||
| 67 | {{/code}} | ||
| 68 | |||
| 69 | The value is the path **without** the ##latex/## prefix, and it goes through the same two-path lookup as a class-derived name: ##latex/custom/custom1## first, then ##latex/default/custom/custom1##. |