/

Rendering

Everything document-generation related lives on karbone.renders, an implementation of the Renders interface.

Template sources

A TemplateSource says where the template comes from:

TemplateSource.bytes(docxBytes, fileName = "invoice.docx")   // in-memory content
TemplateSource.file(Path.of("invoice.docx"))                 // local file, read lazily and cached
TemplateSource.stream("invoice.docx") { openInputStream() }  // opened and read lazily, cached
TemplateSource.id("5f3759df...")                             // a template Carbone already knows

The first three are TemplateSource.Content: their bytes are available locally, and sha256() gives the SHA-256 of that content, which is also the legacy Carbone template id. TemplateSource.id(...) (aliased TemplateSource.Remote) instead references a template already stored on Carbone, by legacy id, versioned template id, or version id — Carbone accepts all three in the same path segment.

Hash-first flow

Rendering from a Content source never requires uploading it up front:

  1. Karbone computes the SHA-256 of the content and calls POST /render/{sha256} directly.
  2. If Carbone already has a template with that hash (because it was uploaded before, by Karbone or anyone else), the render succeeds in one round-trip.
  3. If Carbone answers 404, Karbone uploads the content with templates.upload(...) and retries the render exactly once.

This only applies to legacy (non-versioned) template ids: a TemplateSource.Remote id is used as-is and a 404 on it is raised as KarboneError.TemplateNotFound, never retried.

Render data

RenderData is the data field of the request:

RenderData.raw("""{"number":"F-1"}""")            // pre-serialized JSON, e.g. from Jackson or Gson
RenderData.of(jsonElement)                        // a kotlinx.serialization JsonElement
RenderData.of(myDataClass)                        // any @Serializable value, encoded by Karbone
RenderData.Empty                                  // {} — tags resolve to empty

RenderData.Raw is meant for callers who already produce JSON with another library; Karbone sends it through unmodified. RenderData.of(value) requires value’s type to be @Serializable and uses Karbone’s configured Json (see Configuration) to encode it.

Render options

RenderOptions covers every field Carbone accepts alongside data and the template, built either directly or through the DSL builder:

val options = RenderOptions.build {
  pdf {
    version = PdfVersion.PDF_A_3
    encryptFile = true
    documentOpenPassword = "secret"
    watermark("DRAFT") { opacity = 0.3; rotation = 45.0 }
  }
  lang = "fr-fr"
  timezone = "Europe/Paris"
  reportName = "invoice-{d.number}"
  hardRefresh = true
  header("carbone-egress-header-authorization", "Bearer downstream-token")
}

Each field maps 1:1 to a Carbone request field. The tables below give the shape of each group; the exhaustive list with types and defaults is in the API reference (RenderOptions, PdfOptions, ImageOptions, CsvOptions).

Output format (convertTo)

FormatOptions typeBuilder shortcut
OutputFormat.PdfPdfOptionspdf { ... }
OutputFormat.Jpg / PngImageOptionsjpg(...) / png(...)
OutputFormat.CsvCsvOptionscsv(...)
OutputFormat.DOCX, XLSX, PPTX, ODT, ODS, ODP, HTML, TXT, MD, EPUB, SVG, WEBPnone (OutputFormat.Simple)convertTo = OutputFormat.DOCX

converter picks the PDF engine (LIBRE_OFFICE, ONLY_OFFICE, CHROMIUM) and is only accepted together with a PDF output.

PDF options, by intent

You want to…Use
Produce an archival PDFversion = PdfVersion.PDF_A_1 / PDF_A_2 / PDF_A_3; pdfUaCompliance, useTaggedPdf for accessibility
Protect the documentencryptFile, documentOpenPassword, permissionPassword, restrictPermissions, printing, changes
Stamp itwatermark("text") { anchor; rotation; opacity; color; size; font; fromPage; toPage }, up to PdfOptions.MAX_WATERMARKS; text may use {#PAGE_NUMBER} and {#PAGE_TOTAL}
Control size and pagespageRange (e.g. "1-3"), useLosslessCompression, reduceImageResolution, maxImageResolution
Keep form fields or notesexportFormFields, exportNotes

Image and CSV options

TypeFields
ImageOptionspixelWidth, pixelHeight, colorMode; JPG only: quality (1..100); PNG only: compression (0..9), interlaced, translucent
CsvOptionsfieldSeparator, textDelimiter, characterSet (LibreOffice charset code, "76" = UTF-8)

Localisation and data helpers

FieldEffect
timezoneIANA zone for :formatD; server default Europe/Paris
langLocale such as "fr-fr", drives {t()}, :formatN, :formatC
translationsDictionaries keyed by locale, used with {t()} and lang
enumEnumerations for the convEnum formatter
currencyCurrencyConversion(source, target, rates) for :formatC
complementExtra data reachable through {c.} tags
variableStrPredefined aliases

Output and transport

FieldEffect
reportNameFile name returned in Content-Disposition; may contain Carbone tags ("invoice-{d.number}")
hardRefreshRecompute pagination and table of contents after rendering; requires convertTo
batchBatchOptions(splitBy, output, reportName): one report per array item; requires startAsync with a webhook, rejected as InvalidRequest on render / start
headersExtra HTTP headers, e.g. carbone-egress-header-authorization to relay a token downstream

Sync vs. async

// one round trip, bytes come back directly
val doc = karbone.renders.render(template, data, options)

// two-step: get a RenderId, fetch the file later (no auth needed on download)
val id = karbone.renders.start(template, data, options)
val doc = karbone.renders.download(id)

// asynchronous: Carbone calls the webhook with {success, data: {renderId}} when done
karbone.renders.startAsync(template, data, Webhook("https://example.com/callback"), options)

render calls POST /render/{id}?download=true and returns the file directly. start calls the same endpoint with download=false and returns a RenderId to pass to download (GET /render/{renderId}, unauthenticated; on the cloud the file is kept for one hour and served once). startAsync is required for batch rendering and returns an AsyncRenderAccepted carrying Carbone’s acknowledgement message.

Pure conversion

convert calls POST /render/template: no templating happens, tags in the document are left untouched, only the output format changes.

val pdf = karbone.renders.convert(TemplateSource.bytes(docxBytes), OutputFormat.PDF)

Per the v5 API, omitting data for a conversion skips templating entirely. Some servers still behave like v4 and answer 422 ("Missing data") when data is absent; Karbone detects that and retries once with data: {}, which is equivalent for a document with no Carbone tags. This is transparent to callers.