Template Inputs
Oicana supports two types of inputs. A JSON input takes structured data while binary data can be passed into templates through a blob input.
Inputs are defined in the template manifest. Integrations can list all inputs of a template to, for example, validate input values or offer an editor.
JSON inputs
Section titled “JSON inputs”The type property of the input definition must be json. The only other required property is key.
[[tool.oicana.inputs]]type = "json"key = "data"The following code snippet shows how to set the value for this input from your integration:
import { Template } from '@oicana/browser';
const response = await fetch('/template.zip');const templateBytes = new Uint8Array(await response.arrayBuffer());const template = new Template(templateBytes);
const jsonInputs = new Map<string, string>();jsonInputs.set('data', JSON.stringify({ name: 'Alice' }));
const pdf = template.compile(jsonInputs, new Map());using System.Text.Json.Nodes;using Oicana;using Oicana.Config;using Oicana.Inputs;
var templateBytes = File.ReadAllBytes("template.zip");var template = new Template(templateBytes);
var jsonInputs = new Dictionary<string, JsonNode>{ ["data"] = JsonNode.Parse("""{ "name": "Alice" }""")!,};
var pdf = template.Compile( jsonInputs, new Dictionary<string, BlobInput>(), ExportFormat.Pdf(), new CompilationOptions(CompilationMode.Production));import com.oicana.CompilationMode;import com.oicana.ExportFormat;import com.oicana.Template;import java.nio.file.Files;import java.nio.file.Path;import java.util.Map;
byte[] templateBytes = Files.readAllBytes(Path.of("template.zip"));try (var template = new Template(templateBytes)) { String json = "{\"name\":\"Alice\"}";
byte[] pdf = template.compile( Map.of("data", json), Map.of(), ExportFormat.pdf(), CompilationMode.PRODUCTION);}import { readFile } from 'node:fs/promises';import { Template, Pdf } from '@oicana/node';
const templateBytes = await readFile('template.zip');const template = new Template(templateBytes);
const jsonInputs = new Map<string, string>();jsonInputs.set('data', JSON.stringify({ name: 'Alice' }));
const pdf = template.compile(jsonInputs, new Map(), Pdf);use Oicana\CompilationMode;use Oicana\Template;
$templateBytes = file_get_contents('template.zip');$template = new Template($templateBytes);
try { $pdf = $template->compile( jsonInputs: ['data' => ['name' => 'Alice']], mode: CompilationMode::Production, );} finally { $template->cleanup();}PHP accepts either an associative array (as shown) or a pre-encoded JSON string for each JSON input.
import jsonfrom pathlib import Path
from oicana import CompilationMode, Template
template_bytes = Path("template.zip").read_bytes()
with Template(template_bytes) as template: pdf = template.compile( json_inputs={"data": json.dumps({"name": "Alice"})}, mode=CompilationMode.PRODUCTION, )use std::fs::File;
use oicana::Template;use oicana_input::{CompilationConfig, TemplateInputs};use oicana_input::input::json::JsonInput;
let template_file = File::open("template.zip")?;let mut template = Template::init(template_file)?;
let mut inputs = TemplateInputs::new();inputs.with_config(CompilationConfig::production());inputs.with_input(JsonInput::new( "data", serde_json::json!({ "name": "Alice" }).to_string(),));
let result = template.compile(inputs)?;Blob inputs
Section titled “Blob inputs”Blob inputs can be used for binary data like images. Additional metadata can be used to further specify the type of binary data in the input.
[[tool.oicana.inputs]]type = "blob"key = "logo"As a common use case for blob inputs, images have special support in the oicana Typst package.
To set the value for this input from your integration, pick your language. Each example reads a logo.png file and passes its bytes along with an image_format metadata entry so the oicana-image helper can pick the right decoder:
import { Template, type BlobWithMetadata } from '@oicana/browser';
const templateResponse = await fetch('/template.zip');const templateBytes = new Uint8Array(await templateResponse.arrayBuffer());const template = new Template(templateBytes);
const logoResponse = await fetch('/logo.png');const logo = new Uint8Array(await logoResponse.arrayBuffer());
const blobInputs = new Map<string, BlobWithMetadata>();blobInputs.set('logo', { bytes: logo, meta: { image_format: 'png' },});
const pdf = template.compile(new Map(), blobInputs);using System.Text.Json.Nodes;using Oicana;using Oicana.Config;using Oicana.Inputs;
var templateBytes = File.ReadAllBytes("template.zip");var template = new Template(templateBytes);
var logo = File.ReadAllBytes("logo.png");
var blobInputs = new Dictionary<string, BlobInput>{ ["logo"] = new BlobInput(logo, new BlobMeta { ImageFormat = "png" }),};
var pdf = template.Compile( new Dictionary<string, JsonNode>(), blobInputs, ExportFormat.Pdf(), new CompilationOptions(CompilationMode.Production));import com.oicana.BlobInput;import com.oicana.CompilationMode;import com.oicana.ExportFormat;import com.oicana.Template;import java.nio.file.Files;import java.nio.file.Path;import java.util.Map;
byte[] templateBytes = Files.readAllBytes(Path.of("template.zip"));try (var template = new Template(templateBytes)) { byte[] logo = Files.readAllBytes(Path.of("logo.png"));
byte[] pdf = template.compile( Map.of(), Map.of("logo", new BlobInput(logo, Map.of("image_format", "png"))), ExportFormat.pdf(), CompilationMode.PRODUCTION);}import { readFile } from 'node:fs/promises';import { Template, Pdf, type BlobWithMetadata } from '@oicana/node';
const templateBytes = await readFile('template.zip');const template = new Template(templateBytes);
const logo = await readFile('logo.png');
const blobInputs = new Map<string, BlobWithMetadata>();blobInputs.set('logo', { bytes: logo, meta: { image_format: 'png' },});
const pdf = template.compile(new Map(), blobInputs, Pdf);use Oicana\CompilationMode;use Oicana\Inputs\BlobInput;use Oicana\Template;
$templateBytes = file_get_contents('template.zip');$template = new Template($templateBytes);
try { $logo = file_get_contents('logo.png');
$pdf = $template->compile( blobInputs: [ 'logo' => new BlobInput($logo, ['image_format' => 'png']), ], mode: CompilationMode::Production, );} finally { $template->cleanup();}from pathlib import Path
from oicana import BlobInput, CompilationMode, Template
template_bytes = Path("template.zip").read_bytes()
with Template(template_bytes) as template: logo = Path("logo.png").read_bytes()
pdf = template.compile( blob_inputs={ "logo": BlobInput(data=logo, metadata={"image_format": "png"}), }, mode=CompilationMode.PRODUCTION, )use std::fs::File;
use oicana::Template;use oicana::typst::foundations::{Bytes, Dict, Value};use oicana_input::{CompilationConfig, TemplateInputs};use oicana_input::input::blob::{Blob, BlobInput};
let template_file = File::open("template.zip")?;let mut template = Template::init(template_file)?;
let logo = std::fs::read("logo.png")?;
let mut metadata = Dict::new();metadata.insert("image_format".into(), Value::Str("png".into()));
let mut inputs = TemplateInputs::new();inputs.with_config(CompilationConfig::production());inputs.with_input(BlobInput::new( "logo", Blob { bytes: Bytes::new(logo), metadata, },));
let result = template.compile(inputs)?;Default and Development values
Section titled “Default and Development values”Inputs can define two different fallback values, default and development.
When compiling a template in development mode, input values have the priority
- Explicit input value
developmentvaluedefaultvalue
If you compile in production mode, the development value is ignored:
- Explicit input value
defaultvalue
While developing an Oicana template in a Typst editor, it will be compiled in development mode. It makes sense to define development values for all required inputs of your template to have a functioning preview.
Considering a template with the files development-data.json, default-data.json, development-logo.png, and default-logo.png. It could define the following inputs:
[[tool.oicana.inputs]]type = "json"key = "data"development = "development-data.json"default = "default-data.json"
[[tool.oicana.inputs]]type = "blob"key = "logo"development = { file = "development-logo.png", meta = { image_format = "png", foo = 5, bar = ["development", "two"] } }default = { file = "default-logo.png", meta = { image_format = "png", foo = 5, bar = ["default", "two"] } }The default.meta objects for blob fallback values are optional.
In the preview of an editor, the content of development-data.json and development-logo.png would be used. If compiled in production mode through an Oicana integration, the default fallbacks would be used if the input values are not set programmatically.
Required inputs
Section titled “Required inputs”By default, all inputs are required. If a required input has no value after resolving fallbacks (considering the compilation mode), Oicana will produce a compile error.
You can mark an input as optional by setting required = false:
[[tool.oicana.inputs]]type = "json"key = "extra-data"required = falseOptional inputs without a value will have a none value in the input dictionary returned by the setup function.
This is useful for inputs that templates can handle gracefully when absent, while still getting a clear error for inputs that must always be provided.
Validation configuration
Section titled “Validation configuration”By default, all explicit JSON input values with a schema are validated before compilation. You can control this on two levels.
Per-template default
Section titled “Per-template default”The validate_json_inputs_by_default property in [tool.oicana] controls whether validation for JSON inputs with schemas is enabled. It defaults to true. Setting it to false means the template starts with validation disabled, though integrations can still toggle it at runtime per template instance.
[tool.oicana]manifest_version = 1validate_json_inputs_by_default = falsePer-input
Section titled “Per-input”Each JSON input has an optional validate property that defaults to true. Setting it to false prevents Oicana from compiling a schema validator for that input, even if a schema is defined. This is useful when a schema is only needed for test fuzzing and not for runtime validation.
[[tool.oicana.inputs]]type = "json"key = "data"schema = "data.schema.json"validate = falseNote that validate = false on an input is different from validate_json_inputs_by_default = false on the template. The per-input flag prevents validation entirely, while the template-level flag still allows integrations to change the validation behavior at runtime.
Using inputs in Typst
Section titled “Using inputs in Typst”To access input values in your template, use the setup function from the oicana Typst package:
#import "@preview/oicana:0.1.1": setup
#let read-project-file(path) = read(path, encoding: none)#let (input, oicana-image, oicana-config) = setup(read-project-file)inputis a dictionary of resolved input values, keyed by the input’skeyoicana-imageis a helper function that takes a blob input key and returns a Typst image elementoicana-configcontains compilation metadata likeproduction: true/false
For blob inputs that are images, you can use the oicana-image helper instead of accessing the raw bytes:
#oicana-image("logo", alt: "Company logo")For JSON inputs, access the parsed data directly:
#let name = input.invoice.buyer.name