A company that writes with LaTeX ends up with a document class, a letter class and a presentation theme that share a design and not a line of code. Every fix is then made three times, two of them late and the third never. Here the page is written once. A company declares its colours, faces and logo in one file, and the document template, the letter class and the presentation theme read that one file. Nothing in the suite carries a colour value, a font name or a file name of any company, which is what lets it be published while the companies stay private. Every measure of the page follows from a measurement or from a named definition: the room the head and the foot need is taken from the boxes they really build, one line of the body text stands between the head and the text and between the text and the foot, a heading never stands alone at the foot of a page, a picture takes the size of the family, and a Markdown file reaches the same page as the same document written in LaTeX. The suite ships with a company that does not exist, Nordwind AG, so that it builds and is measured anywhere: three colours, the TeX Gyre families every installation carries, and an icon drawn in TikZ. 574 checks over five measurements, all of them on the rendered page or on the build log rather than on the source. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Business Suite
Business Suite provides shared LaTeX templates for reports, letters and Beamer presentations. A single company package defines the corporate identity—colours, fonts, logo and contact details—and all templates use that configuration.
The suite contains no company-specific colour values, font names or asset paths. Corporate identities therefore remain private while the reusable templates can be published and tested independently.
Components
| Component | Purpose |
|---|---|
business-identity.sty |
Defines colours, fonts, logos and shared commands |
business-suite.sty |
Formats reports and other standard LaTeX documents |
business-brief.cls |
Provides business letters based on g-brief2 |
business-beamer.sty |
Provides the shared Beamer presentation design |
md2pdf.py |
Converts Markdown to a branded PDF through Pandoc and XeLaTeX |
Requirements
- A LaTeX installation with XeLaTeX
- Pandoc and Python 3 for Markdown conversion
- Inkscape when a document contains SVG images
Company-specific fonts and packages must also be available to XeLaTeX.
Quick Start
Install the Suite
Link the suite and converter into the local TeX and executable trees. Symbolic links keep the installation aligned with the working copy.
mkdir -p ~/texmf/tex/latex ~/bin
ln -s ~/git/mrw/business-suite/texmf/tex/latex/business-suite \
~/texmf/tex/latex/business-suite
ln -s ~/git/mrw/business-suite/bin/md2pdf.py ~/bin/md2pdf.py
Install a company identity in the same way:
ln -s ~/git/nordwind/latex ~/texmf/tex/latex/nordwind
For builds that must not depend on the local installation, set TEXINPUTS to the source tree. The npm scripts in this repository do this automatically.
Create a Company Identity
Each company has one package, such as nordwind.sty. It defines the palette, maps colours to semantic roles, configures the fonts and registers the logo before loading the document template.
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{nordwind}[2026/09/21 Nordwind AG corporate identity]
\RequirePackage{business-identity}
\definecolor{nordwind-deep}{HTML}{14425C}
\definecolor{nordwind-sea}{HTML}{3E8EA8}
\businesscolours{
brand = nordwind-deep,
title = nordwind-deep,
titlemeta = nordwind-deep!60,
text = black,
running-strong = nordwind-deep,
running-quiet = nordwind-deep!55,
folio = nordwind-deep!55,
caption = nordwind-deep!70,
rowrule = nordwind-deep!25,
heading-bg = nordwind-deep,
heading-fg = white,
border = nordwind-sea,
thick-border = nordwind-deep,
black-border = black,
}
\setmainfont{TeX Gyre Pagella}
\setsansfont{TeX Gyre Heros}
\setmonofont{TeX Gyre Cursor}[Scale=MatchLowercase]
\businessfonts{
default = rm,
symbols = DejaVu Sans,
smallcaps = false,
}
\businessidentity{
name = Nordwind,
url = https\://nordwind.example,
address = https\://nordwind.example,
lockup = icon+text,
icon = nordwind-icon,
}
\DeclareOption*{\PassOptionsToPackage{\CurrentOption}{business-suite}}
\ProcessOptions\relax
\RequirePackage{business-suite}
\endinput
Create a Document
Load the company package instead of the generic template:
\documentclass[a4paper,10pt]{article}
\usepackage{nordwind}
\title{Annual Report}
\author{Erika Muster}
\date{\today}
\begin{document}
\maketitle
\section{Current Position}
Dolor sit amet.
\end{document}
The template automatically applies the corporate logo, page header, footer, title block, heading rules, table widths, image sizing, PDF outline and PDF metadata.
Convert Markdown
md2pdf.py --identity nordwind report.md
The equivalent npm command is:
npm run md2pdf -- --identity nordwind report.md
To provide a company-specific command, add a wrapper to the PATH:
#!/bin/sh
exec md2pdf.py --identity nordwind "$@"
Corporate Identity
Colour Roles
Company packages define concrete colours and assign them to semantic roles. Templates use only these roles.
| Role | Usage |
|---|---|
brand |
Logo and primary accents |
text |
Body text |
title |
Document title |
titlemeta |
Author and date |
running-strong |
First line of the running header |
running-quiet |
Second line of the running header |
folio |
Page number |
caption |
Image captions |
rowrule |
Table row separators |
success, fail |
Status marks |
heading-bg, heading-fg |
Heading bands and presentation bars |
border, thick-border, black-border |
Document frames |
A role can reference a palette colour directly or use an xcolor mixture such as nordwind-deep!50!black. The success and fail roles default to green and red and do not have to be part of the company palette.
Font Settings
The company package loads and configures fontspec; \businessfonts tells the templates how to use the configured families.
| Key | Meaning |
|---|---|
default |
Default document family: rm or sf |
emphasis |
Face used for emphasis; defaults to bold when omitted |
symbols |
Fallback family for missing symbols |
smallcaps |
Set to false if the selected font has no small capitals |
microtype |
Set to false to disable microtypography |
protrusion |
Set to true to allow punctuation to extend into the margin |
The font encoding remains associated with the selected family. This prevents a class that preselects a family using T1 encoding from silently falling back to Computer Modern.
Logo Configuration
The identity can reference an image file or a drawing command:
\businessidentity{logo = company-logo}
\businessidentity{icon code = \companydrawing}
Image filenames omit the extension. The suite checks PDF, EPS, PNG and JPEG in that order. Convert SVG logos before the build.
A drawing command receives the requested height and must produce a box of exactly that height. TikZ drawings should freeze their bounding box with \pgfresetboundingbox and an explicit \path[use as bounding box]. The selected logo colour is available as \businesslogocolour.
The lockup setting defines the logo structure:
banner: the asset already contains the icon and company name.icon+text: the suite places the company name beside the icon.
With a banner lockup, the tagline appears below the complete banner and aligns with its right edge.
Tagline
Add a second line below the company name with tagline:
\businessidentity{
name = Nordwind,
tagline = Vernunft und Freiheit,
}
The following optional keys change the default appearance:
\businessidentity{
name face = {\bfseries},
tagline face = {\mdseries},
tagline color = business-running-quiet,
}
The name is 80% of the requested logo height. The tagline is set to the size at which it is exactly as wide as the name above it, so both lines share one left edge and one right edge whatever the sentence says. The gap between them is 20% of the name size and taglinegap changes it.
The icon takes the height of the text block beside it and keeps the proportions of its own drawing. A mark with a height of its own matches the text at one type size and at no other.
Each measured size has a key that overrides it:
| Key | Overrides |
|---|---|
taglinefactor |
the tagline size, as a share of the name instead of its width |
iconheight |
the icon height, instead of the height of the text block |
iconwidth |
the icon width; the height follows from the drawing |
Never give both icon sizes at once: two fixed sizes distort the mark. A width also reaches a drawn icon, not only an image file. The suite sets the icon once, measures its width and scales the height by the ratio.
Templates request the tagline where sufficient space is available. Logos in running text remain on one line unless tagline is explicitly requested.
Shared Commands
Logos
\logo
\logo[height=2cm]
\logo[address]
\logo[icon]
\logo[text]
\logo[nolink]
\logo[color=business-text]
\logo[tagline]
\logo[tagline,address]
Use \businesslogo where another package owns \logo; Beamer, for example, uses \logo to set the presentation logo.
By default, logos are 1.25 em high in running text and 2 em high in template headers. The company name is 80% of the logo height. Gaps beside the icon and above the address are 10% of the logo height. Each value can be overridden.
The address line is scaled to the exact width of the logo or lockup above it. The icon is vertically centred beside the name while the name retains the surrounding text baseline.
Status Marks
\success and \fail render white status symbols on green and red backgrounds. The Unicode characters ✅, ❌, ✓, ✗, ✔ and ✘ map to the same marks, which allows Markdown sources to use them directly.
Other common symbols—including arrows, information signs, warnings, stars and checkboxes—use the active text font when available and the configured symbol family otherwise. Unsupported characters are omitted without adding a gap or log message.
Images
Use \businessgraphic for template-controlled image sizing:
\businessgraphic{diagram}
\businessgraphic[width=4cm]{diagram}
Default sizing depends on orientation:
- Portrait images use up to the full line width and half the text height.
- Landscape images use up to the full line width and the free height remaining on the page.
- Images retain their aspect ratio and are centred.
- If an image would leave no more than 25% of a page empty, it moves to the next page. Otherwise, it is reduced to fit the current page.
- Vector graphics may use the full calculated size. Raster images are enlarged by at most a factor of two.
| Option | Default | Meaning |
|---|---|---|
portraitwidth |
1 |
Maximum share of the line width for portrait images |
portraitheight |
0.5 |
Maximum share of the text height for portrait images |
landscapewidth |
1 |
Maximum share of the line width for landscape images |
landscapeheight |
1 |
Maximum share of the remaining page height |
pictureempty |
0.25 |
Maximum empty page share before an image is reduced |
picturezoom |
2 |
Maximum enlargement factor for raster images |
Markdown conversion applies these rules automatically.
Footer Content
The outer footer shows the current page and total page count. Add optional content to the inner footer in the preamble:
\businessfootleft{\includegraphics[height=2em]{product}}
The footer may contain a product logo, classification or series name. Its reserved height is measured from the rendered content. Define it before \begin{document} because page geometry cannot be recalculated afterwards.
Columns
businesscolumns creates equal-width columns. Their widths follow from the current line width, and the gap between columns equals one body-text line.
\begin{businesscolumns}[3]
\businesscell{First}
\businesscell{Second}
\businesscell{Third}
\end{businesscolumns}
Use \businessuse{<name>} to include a reusable block from the TeX tree, such as a sender block or terms page.
Tables
Use standard LaTeX table environments with the shared table commands:
\begin{tabular}{lr}
\toprule
\businesstablehead{Figure} & \businesstablehead{Value} \\
\midrule
Turnover & 4.2 million \\ \businessrowrule
People & 31 \\
\bottomrule
\end{tabular}
\businesstablehead{...}formats a header cell.\businessrowruledraws the separator between rows.
Keep an introductory sentence with the following table or list using:
\businessleadin[2]
The annual figures:
\businesstogether
The optional argument specifies the number of lines occupied by the introduction. The default is six. The Markdown converter inserts both commands where required.
Document Layout
Header and Footer
The first page shows the corporate logo and address. Subsequent pages use two running-header lines and display the current and total page count in the footer.
The suite measures the actual header and footer content. It leaves one body-text line between the header and text area and the same distance between the text area and footer. The headsep option changes the upper distance; the lower distance is derived from it.
Page Composition
The first text line begins at the upper edge of the text block. Paragraph spacing may stretch by up to half a line so that pages end on a consistent baseline. The pagestretch option controls this flexibility.
Headings remain attached to their following content. Tables and images use the current line width and available page height instead of fixed dimensions from a particular company design.
Business Letters
business-brief.cls extends g-brief2 with the corporate identity and shared logo handling. Create a company-specific class:
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{nordwind-brief}[2026/09/21]
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{business-brief}}
\ProcessOptions\relax
\LoadClass{business-brief}
\RequirePackage[plain]{nordwind}
\RetourAdresse{Nordwind~AG~-~Hafenstrasse~4~-~8000~Zürich}
\NameZeileA{Nordwind AG}
\NameZeileB{Hafenstrasse 4}
\NameZeileC{CH~--~8000 Zürich}
\InternetZeileA{https\://nordwind.example}
\InternetZeileB{post\@nordwind.example}
\BankZeileA{CH00 0000 0000 0000 0000 0}
\Gruss{Mit freundlichen Grüssen}{2em}
\endinput
The class preserves the existing g-brief2 field names. It adds:
| Command | Purpose |
|---|---|
\Unterschriftsbild{<file>}{<name>} |
Inserts a printed signature instead of reserving space for a handwritten one |
\businessnowindow |
Removes the envelope window, folding marks and return line, then reduces the address field to the current address height |
\businesslogoheight |
Sets the header logo height, capped at the available 20 mm |
The first page places the logo and address above the sender block. Later pages place the page number and date on the left and the logo on the right.
Example:
\documentclass[english]{nordwind-brief}
\Datum{\today}
\Adresse{Ms\\Erika Muster\\Beispielweg 7\\8001 Zurich}
\Betreff{Our offer for the harbour}
\Anrede{Dear Ms Muster}
\Unterschrift{Hans Beispiel\\Managing Director}
\begin{document}
\begin{g-brief}
We are pleased to send you our offer.
\end{g-brief}
\end{document}
Presentations
Create a two-line company theme:
% beamerthemenordwind.sty
\RequirePackage[plain]{nordwind}
\RequirePackage{business-beamer}
Load it from a presentation with \usetheme{nordwind}:
\documentclass[12pt]{beamer}
\usetheme{nordwind}
\title{The Harbour}
\author{Hans Beispiel}
\date{\today}
\begin{document}
\begin{frame}[plain]
\titlepage
\end{frame}
\section{Current Position}
\begin{frame}
\frametitle{Project Status}
\begin{itemize}
\item \success{} Permission granted
\item \fail{} Funding open
\end{itemize}
\end{frame}
\end{document}
The theme uses Beamer's sidebar outer theme. Configure it with Beamer's standard width, height, left, right, hideothersubsections and hideallsubsections options.
Business Suite adds:
| Option | Effect |
|---|---|
titleprefix |
Adds content before every frame title |
canvas=brand |
Uses the brand colour as the slide background |
nosectionframe |
Disables the opening frame for each section |
Markdown Converter
md2pdf.py passes the selected identity and document template to Pandoc, then builds the generated LaTeX with XeLaTeX.
| Option | Default | Effect |
|---|---|---|
--identity <name> |
Required | Selects the company identity package |
--out-dir <dir> |
Source directory | Sets the PDF output directory |
--lang <tag> |
auto |
Controls hyphenation; auto detects the language unless the document defines lang: |
--paper <name> |
a4paper |
Sets the paper format |
--fontsize <pt> |
10pt |
Sets the document font size |
--toc |
Off | Adds a table of contents |
--highlight |
Off | Enables Pandoc syntax highlighting in code blocks |
--keep-tex |
Off | Retains the generated LaTeX and build files |
Use $E = mc^2$ for inline formulas and $$...$$ for display formulas. The converter joins multi-line formulas before Pandoc parses them and reports this correction. This avoids Markdown interpreting a line containing only = as a level-one heading underline.
Development
npm run build # Build all examples
npm test # Run all measurement tests
npm run clean # Remove generated files
The examples in examples/ use nordwind.sty, a fictional company identity built from portable TeX Gyre fonts. The dedicated name avoids collisions with existing TeX Live packages.
Additional project documentation:
Design Decisions
No Company Data
Reusable templates contain no company-specific colour values, font names or asset paths. Tests scan all template sources and fail when they find such values.
Measure Actual Content
The suite calculates logo height, address width, header and footer space, table-column widths and the space required by connected blocks from the rendered content. Fixed values derived from one font, paper format or company identity do not remain correct for another.
Probe Document
The Pandoc table filter runs before LaTeX exists and therefore cannot directly inspect \linewidth. The converter builds a probe document with the selected company identity, then reads \linewidth and the width of a reference line from that document. This prevents table widths from depending on constants copied from another identity.
Single Definition
Each company value has one authoritative definition in its identity package. The document, letter and presentation templates consume that package instead of copying its values. Tests therefore verify that templates contain no company-specific values, rather than checking whether duplicated values still match.
Shared table commands such as \businessrowrule and \businesstablehead belong to the identity layer because tables can appear in documents, letters and presentations. Loading an identity with the plain option retains these shared commands but omits document geometry and page styles.
Document Language
The template does not set the document language. When Babel is loaded, Hyperref obtains the language from Babel. Setting it again would produce an Option 'pdflang' has already been used warning. Tests verify the resulting language in the document's XMP metadata.
Native Beamer Sidebar
The presentation template uses Beamer's maintained sidebar outer theme instead of carrying a private copy.
Letter Address Field
Window-envelope letters retain the physical 63 mm address field. With \businessnowindow, the field shrinks to the height required by the current recipient address and makes the remaining space available to the letter body.
Licence
MIT, see LICENSE. Use it, change it, sell what you build with it; keep the copyright notice in a copy.
Every source file of the suite carries the notice and the SPDX identifier in its first lines. A LaTeX package is installed file by file: what a company copies into its own tree is business-suite.sty, and the licence file does not travel with it.