Skip to content

DocumentArtefact

DocumentArtefact manifests define standalone PDF documents rendered from Markdown source files. Unlike ReportArtefact which renders data-driven layouts, DocumentArtefact is designed for text-heavy documents like manuals, specifications, or narrative reports.

This is the smallest well-formed DocumentArtefact: the two required fields (format and sources) plus the fields a real document deliverable needs - its output filename, its title, the locale, a table of contents and page numbers.

apiVersion: bino.bi/v1alpha1
kind: DocumentArtefact
metadata:
  name: technical_manual
spec:
  format: a4
  locale: en
  filename: technical-manual.pdf
  title: "Technical Manual"
  tableOfContents: true
  displayHeaderFooter: true
  sources:
    - ./docs/introduction.md
    - ./docs/installation.md
    - ./docs/configuration.md

Entries in sources are file paths or glob patterns; the older object form (- file: ./docs/intro.md) is deprecated and should not be used in new manifests.

All attributes are listed in the Attribute Reference below.

  • spec.format - Required page size: a4, a5, letter, or legal. A manifest without it fails schema validation; the loader falls back to a4 with a warning.
  • spec.orientation - portrait or landscape. Default: portrait.
  • spec.locale - Document locale (e.g., en, de). Default: de.
  • spec.filename - Output filename (relative to output directory). Without it the PDF is named after metadata.name (<name>.pdf).
  • spec.title - Document title; used in PDF metadata and default header.
  • spec.author - Optional author name for PDF metadata.
  • spec.subject - Optional subject for PDF metadata.
  • spec.keywords - Optional list of keywords for PDF metadata.
  • spec.sources - Required list of Markdown file paths or glob patterns to include in the document, at least one entry. Files matching glob patterns are sorted alphabetically for deterministic ordering. Supports patterns like ./docs/*.md or ./docs/**/*.md.
  • spec.stylesheet - Optional path to a custom CSS file for styling.
  • spec.pageBreakBetweenSources - Insert page breaks between source files. Default: true. With more than one source the loader forces it to true.

You can use glob patterns to include multiple markdown files:

sources:
  - ./docs/**/*.md # All .md files in docs folder (recursive)
  - ./appendix/*.md # All .md files directly in appendix folder
  - ./README.md # Single file

Files are deduplicated if patterns overlap, and sorted alphabetically within each pattern expansion.

  • spec.tableOfContents - Generate a table of contents from headings. Default: false.
  • spec.tocNumbering - Add hierarchical chapter numbers to TOC entries (e.g. 1, 1.1, 1.1.1, 1.1.1 a). Only applies when tableOfContents is enabled. Default: true.

When enabled, the table of contents is automatically generated from all h1-h6 headings that have an id attribute (standard Markdown headings get IDs automatically). The TOC includes page numbers for each heading, calculated during a two-pass rendering process.

The TOC appears at the beginning of the document (after any front cover pages) and includes a page break after it. In the PDF, each TOC entry is a link to its heading.

  • spec.math - Enable LaTeX math rendering via KaTeX. Default: true.

When enabled, you can include mathematical expressions in your Markdown:

  • Inline math: Use single dollar signs $...$ for inline expressions like $E = mc^2$
  • Block math: Use double dollar signs $$...$$ for display equations
The quadratic formula is $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$.

For summations, use display mode:

$$\sum_{i=1}^n x_i = x_1 + x_2 + \cdots + x_n$$

Math is rendered server-side using KaTeX, so no JavaScript is required in the output PDF.

  • spec.displayHeaderFooter - Enable header and footer in the PDF. Default: false.
  • spec.headerTemplate - Custom HTML template for the header.
  • spec.footerTemplate - Custom HTML template for the footer.
  • spec.marginTop - Top margin reserved for the header (e.g., 20mm). Default: 20mm.
  • spec.marginBottom - Bottom margin reserved for the footer (e.g., 15mm). Default: 15mm.

When displayHeaderFooter is enabled without custom templates, default templates are used:

  • Header: Document title centered
  • Footer: Current date on the left, page number on the right

Templates use Chromium’s PDF header/footer format. Available CSS classes:

  • .title - Document title
  • .date - Current date
  • .pageNumber - Current page number
  • .totalPages - Total page count
  • .url - Document URL

Example custom footer:

spec:
  displayHeaderFooter: true
  marginBottom: "15mm"
  footerTemplate: |
    <div style="font-size: 10px; width: 100%; text-align: center;">
      <span class="pageNumber"></span> / <span class="totalPages"></span>
    </div>
  • spec.cover.front - Pages printed before the table of contents and the content.
  • spec.cover.back - Pages printed after the content.

A cover written as the first markdown source would get page number 1, the header and the footer, and would shift every page number in the table of contents. Cover pages avoid that: each one is printed on its own and merged around the content, so it has no page number, no header and no footer, and the content still starts at page 1.

Each entry in front and back is one page and sets exactly one source:

spec:
  cover:
    front:
      - template: classic
        params: { subtitle: "Operations guide", logo: companyLogo, accent: "#0b5394" }
      - html: |
          <div class="note">{{ .Date }} · {{ .Author }}</div>
        css: |
          .note { position: absolute; bottom: 3cm; left: 2cm; }
    back:
      - pdf: legalNotice # Asset of type file, mediaType application/pdf
        page: 2 # 1-based, default 1
      - image: backCover # Asset of type image
        fit: cover # cover | contain, default cover
  • image names an Asset of type image. It fills the whole page; fit: cover crops it to fill, fit: contain shows the whole image.
  • pdf names an Asset of type file with mediaType: application/pdf. One page is taken from it (page, default 1) and keeps its own page size; a size that differs from the document format is logged as a warning. localPath, inlineBase64 and remoteURL sources work; a remote PDF is downloaded during the build.
  • template: classic is a built-in cover with the title, an optional subtitle, the author, the date, an optional logo and an accent bar. It reads params.subtitle, params.logo (an Asset name) and params.accent (a CSS color).
  • html is your own page body.

html and template accept css, added after the template styles, and params.

html is a Go html/template. The classic template uses the same data:

Field Value
.Title spec.title
.Author spec.author
.Subject spec.subject
.Date Build date in spec.locale: 22. Oktober 2026 (de), October 22, 2026 (en), 2026-10-22 for other locales
.Params.x The entry’s params.x

.Date uses SOURCE_DATE_EPOCH when it is set, so the cover date does not change between builds. asset:<name> URLs in html and css resolve to Assets, as in markdown. They are resolved in src, href, poster, data and srcset attribute values and in CSS url(...); an unknown Asset name there fails the build. Other text, such as a .asset:hover selector, is left alone:

- html: |
    <img class="hero" src="asset:heroImage">
    <h1>{{ .Title }}</h1>
  css: |
    .hero { width: 100%; height: 60%; object-fit: cover; }

The classic template is styled with CSS custom properties, so a css block can restyle it without copying the template:

- template: classic
  css: |
    :root {
      --bn-cover-font: Georgia, serif;
      --bn-cover-title-size: 40pt;
      --bn-cover-bg: #f4f1ea;
    }

Available properties: --bn-cover-accent, --bn-cover-bg, --bn-cover-fg, --bn-cover-muted, --bn-cover-font, --bn-cover-padding, --bn-cover-bar-width, --bn-cover-title-size, --bn-cover-subtitle-size, --bn-cover-meta-size, --bn-cover-logo-height. The elements use the classes bn-cover, bn-cover-bar, bn-cover-logo, bn-cover-title, bn-cover-subtitle, bn-cover-meta, bn-cover-author and bn-cover-date.

  • Every cover page is a standalone page in the document’s format and orientation, with margin 0 (full bleed). It does not load spec.stylesheet or the document’s body styles.
  • Content that does not fit on one page is cut off after the first page, with a warning.
  • A missing Asset, a wrong Asset type or media type, and a page beyond the end of the PDF fail the build.
  • A signed document is signed after the cover pages are merged in.
  • spec.signingProfile - Optional reference to a SigningProfile manifest for digital signatures.

DocumentArtefact supports standard Markdown plus:

Use the :ref[Kind:name] syntax to embed report components (tables, charts) in your document:

## Sales Overview

The following table shows quarterly sales:

:ref[Table:quarterly_sales]

And here's the trend chart:

:ref[ChartTime:sales_trend]

Referenced components must be defined as Table, ChartTime, Tree, ChartStructure, Text, Image, LayoutCard, or Grid manifests in your project, or as a component kind from a plugin.

Add captions to referenced components using the {caption="..."} syntax:

:ref[Table:quarterly_sales]{caption="Table 1: Quarterly Sales by Region"}

:ref[ChartTime:revenue_trend]{caption="Figure 2: Revenue Growth 2020-2024"}

This wraps the component in an HTML <figure> element with a <figcaption>, which:

  • Centers the caption below the component
  • Prevents page breaks inside the figure
  • Uses italic styling for the caption text

Refs without a caption are rendered as before, without the figure wrapper.

You can embed images from Asset manifests using the asset: URL scheme in standard Markdown image syntax:

![Company Logo](asset:companyLogo)

The asset: prefix is resolved at build time to the actual URL based on the asset’s source type (localPath, remoteURL, or inlineBase64). See the Asset reference for details.

Fenced code blocks with a language hint are syntax-highlighted at build time with chroma:

```sql
SELECT * FROM sales WHERE year = 2024;
```
  • The default theme is github: a very light grey background with dark, high-contrast token colors that print well.
  • A block without a language hint, or with a language chroma does not know, stays a plain grey block. The language is never guessed from the content.
  • Tokens are marked with CSS classes (for example .chroma .k for keywords). The theme stylesheet is loaded before your custom stylesheet, so your rules override the theme colors.
  • Long lines wrap inside the block instead of running off the page. This applies to all code blocks, highlighted or not.

Use spec.codeHighlight to pick another theme, turn on line numbers, or disable highlighting:

spec:
  codeHighlight:
    theme: xcode # any chroma style name, or "none" to disable highlighting
    lineNumbers: true # default: false

An unknown theme name fails the build, and the error lists all valid names.

bino preview serves every DocumentArtefact at /doc/<name>. The route is reachable from the artefact dropdown in the toolbar and from the Documents list on the All Pages view. Edits to the markdown sources, the manifest, or any embedded component’s data reload the open page in place - long documents keep their scroll position.

The preview is exact for content and approximate for print geometry:

  • Exact: the rendered markdown, math, embedded :ref components with live data, and your custom stylesheet.
  • Approximated: the page width follows format and orientation; page breaks between sources show as dashed markers; with displayHeaderFooter the reserved margin bands appear as labeled placeholders sized by marginTop/marginBottom.
  • Cover pages show above and below the content, each in a frame sized to the page, so their CSS stays apart from the document CSS as in the PDF. A pdf cover shows a placeholder card (“PDF page N of asset X”).
  • Only in a PDF (the toolbar’s PDF button or bino build): real pagination, the page numbers in the table of contents, the separately paginated TOC pages with Roman numerals (the preview page shows the TOC inline, without page numbers), and the actual header/footer templates. Signing happens only in bino build.

The toolbar shows the document’s settings (format, orientation, locale, chapter count, TOC, header/footer), the search box jumps to headings, and the PDF button builds the document as a PDF marked PREVIEW and shows it in the browser (see PDF preview). Run bino build --artefact <name> for the final file.

In the VS Code preview, Cmd/Ctrl+click on prose opens the markdown source file it came from; on an embedded component it opens the component’s YAML manifest.

apiVersion: bino.bi/v1alpha1
kind: DocumentArtefact
metadata:
  name: readme
spec:
  format: a4
  filename: readme.pdf
  title: "Project README"
  sources:
    - ./README.md
apiVersion: bino.bi/v1alpha1
kind: DocumentArtefact
metadata:
  name: user_guide
spec:
  format: a4
  orientation: portrait
  filename: user-guide.pdf
  title: "User Guide"
  author: "Documentation Team"
  tableOfContents: true
  displayHeaderFooter: true
  marginTop: "20mm"
  marginBottom: "15mm"
  sources:
    - ./docs/getting-started.md
    - ./docs/features.md
    - ./docs/troubleshooting.md
  pageBreakBetweenSources: true
apiVersion: bino.bi/v1alpha1
kind: DocumentArtefact
metadata:
  name: quarterly_report
spec:
  format: a4
  filename: q4-report.pdf
  title: "Q4 2024 Report"
  tableOfContents: true
  displayHeaderFooter: true
  marginTop: "20mm"
  marginBottom: "15mm"
  sources:
    - ./narrative/executive-summary.md
    - ./narrative/financial-analysis.md
    - ./narrative/outlook.md

Where executive-summary.md might contain:

# Executive Summary

This quarter showed strong growth across all regions.

## Revenue by Region

:ref[Table:revenue_by_region]{caption="Table 1: Revenue by Region (in millions)"}

## Growth Trend

:ref[ChartTime:quarterly_growth]{caption="Figure 1: Quarterly Growth Rate"}
apiVersion: bino.bi/v1alpha1
kind: DocumentArtefact
metadata:
  name: branded_manual
spec:
  format: a4
  filename: manual.pdf
  title: "Product Manual"
  stylesheet: ./styles/brand.css
  tableOfContents: true
  displayHeaderFooter: true
  marginTop: "25mm"
  marginBottom: "20mm"
  headerTemplate: |
    <div style="font-size: 10px; width: 100%; border-bottom: 1px solid #ccc; padding-bottom: 5px;">
      <span style="float: left;">ACME Corp</span>
      <span style="float: right;" class="title"></span>
    </div>
  footerTemplate: |
    <div style="font-size: 9px; width: 100%; text-align: center; color: #666;">
      Page <span class="pageNumber"></span> of <span class="totalPages"></span> | Confidential
    </div>
  sources:
    - ./manual/introduction.md
    - ./manual/usage.md
    - ./manual/reference.md
apiVersion: bino.bi/v1alpha1
kind: DocumentArtefact
metadata:
  name: complete_docs
spec:
  format: a4
  filename: complete-documentation.pdf
  title: "Complete Documentation"
  tableOfContents: true
  pageBreakBetweenSources: true
  sources:
    - ./docs/**/*.md # Include all markdown files recursively
apiVersion: bino.bi/v1alpha1
kind: DocumentArtefact
metadata:
  name: research_paper
spec:
  format: a4
  filename: research-paper.pdf
  title: "Statistical Analysis Methods"
  author: "Research Team"
  tableOfContents: true
  math: true
  sources:
    - ./paper/abstract.md
    - ./paper/methodology.md
    - ./paper/results.md
    - ./paper/conclusion.md

Where methodology.md might contain:

# Methodology

We use the standard deviation formula:

$$\sigma = \sqrt{\frac{1}{N}\sum_{i=1}^N (x_i - \mu)^2}$$

The correlation coefficient $r$ is calculated as:

$$r = \frac{\sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_{i=1}^n (x_i - \bar{x})^2 \sum_{i=1}^n (y_i - \bar{y})^2}}$$
Attribute Type Required Default Description
apiVersion string yes - Must be bino.bi/v1alpha1.
kind string yes - Must be DocumentArtefact.
metadata.name string yes - Unique identifier. Also the fallback output filename (<name>.pdf) and the preview route /doc/<name>.
metadata.labels object no - Key-value pairs for categorization and constraint matching.
metadata.annotations object no - Arbitrary key-value metadata, not used by the system.
metadata.description string no - Free-form description.
metadata.constraints array no - Conditional inclusion rules. See Constraints.
Attribute Type Required Default Description Sample
spec.format string yes - (loader falls back to a4 with a warning) Page format of the rendered document. Values: a4, a5, letter, legal. format: a4
spec.orientation string no portrait Page orientation. Values: portrait, landscape. orientation: portrait
spec.locale string no de Locale of the document, used for i18n and the bn-context element (for example en, de, fr). locale: en
spec.filename string no <metadata.name>.pdf Output filename of the generated PDF, relative to the output directory. filename: technical-manual.pdf
spec.title string no - Human-readable title, stored in PDF metadata and used by the default header template. title: "Technical Manual"
spec.author string no - Author name stored in the PDF metadata. author: "Engineering Team"
spec.subject string no - Subject line stored in the PDF metadata. subject: "Product Documentation"
spec.keywords array of strings no - Keywords stored in the PDF metadata; entries must be unique. keywords: ["manual", "technical", "documentation"]
spec.sources array of strings yes - Ordered list of markdown files to include, at least one entry. Paths are relative to the manifest and may be glob patterns. An array of { file: ... } objects is accepted but deprecated. sources: [./docs/introduction.md]
spec.sources[] string - - A single markdown path or glob pattern. - ./docs/**/*.md
spec.sources[].file string yes (in the deprecated object form) - Path to a markdown file, relative to the manifest file. Use plain strings instead. - file: ./docs/introduction.md
spec.stylesheet string no - Path to a custom CSS stylesheet, relative to the manifest file. stylesheet: ./styles/manual.css
spec.tableOfContents boolean no false Generates a table of contents from the markdown headings. tableOfContents: true
spec.tocNumbering boolean no true Adds hierarchical chapter numbers to TOC entries (1, 1.1, 1.1.1). Only applies when tableOfContents is enabled. tocNumbering: false
spec.math boolean no true Enables LaTeX math rendering via KaTeX ($...$ inline, $$...$$ display). math: true
spec.codeHighlight object no highlighting on, github theme, no line numbers Syntax highlighting of fenced code blocks. codeHighlight: { theme: xcode }
spec.codeHighlight.theme string no github A chroma style name, or none to disable highlighting. An unknown name fails the build. theme: none
spec.codeHighlight.lineNumbers boolean no false Prints a line number in front of each line of a highlighted code block. lineNumbers: true
spec.cover object no - Cover pages before and after the content, without page number, header or footer. cover: { front: [{ template: classic }] }
spec.cover.front array no - Cover pages printed before the table of contents and the content. front: [{ image: hero }]
spec.cover.back array no - Cover pages printed after the content. back: [{ pdf: notice, page: 2 }]
spec.cover.*[].image string one source required - Asset of type image, shown full bleed. image: hero
spec.cover.*[].fit string no cover How the image fills the page. Values: cover, contain. Only with image. fit: contain
spec.cover.*[].pdf string one source required - Asset of type file with mediaType: application/pdf. One page is used. pdf: notice
spec.cover.*[].page integer no 1 Page of the PDF to use, starting at 1. Only with pdf. page: 2
spec.cover.*[].template string one source required - Built-in cover template. Values: classic. template: classic
spec.cover.*[].html string one source required - Cover body as a Go html/template with .Title, .Author, .Subject, .Date, .Params. html: "<h1>{{ .Title }}</h1>"
spec.cover.*[].css string no - CSS for this cover page. Only with template and html. css: ".x { color: red; }"
spec.cover.*[].params object no - Values for .Params. The classic template reads subtitle, logo, accent. Only with template and html. params: { subtitle: Guide }
spec.pageBreakBetweenSources boolean no true Inserts a page break between each source file. The loader forces true when more than one source is present. pageBreakBetweenSources: true
spec.displayHeaderFooter boolean no false Displays a header and footer on each page, using the default templates when none are given. displayHeaderFooter: true
spec.headerTemplate string no document title, centered Custom HTML template for the page header. Supports the template classes date, title, url, pageNumber, totalPages. See Custom header/footer templates. headerTemplate: '<div class="title"></div>'
spec.footerTemplate string no date on the left, page number on the right Custom HTML template for the page footer. Supports the same template classes as headerTemplate. See Custom header/footer templates. footerTemplate: '<div><span class="pageNumber"></span></div>'
spec.marginTop string no 20mm Top margin reserved for the header (for example 20mm, 1in). Only used when displayHeaderFooter is true. marginTop: "20mm"
spec.marginBottom string no 15mm Bottom margin reserved for the footer (for example 15mm, 0.5in). Only used when displayHeaderFooter is true. marginBottom: "15mm"
spec.signingProfile string no - Name of a SigningProfile manifest used to digitally sign the generated PDF. signingProfile: corporateSigner

See ReportArtefact for data-driven page layouts instead of markdown sources.