Pular para o conteúdo principal

Criando um documento YAML passo a passo

Este guia ensina a escrever um template filemaker: "1.0" do zero. O YAML é estrito: um nome de campo desconhecido, uma unidade ausente ou uma combinação inválida produz erro. Isso evita que um erro de digitação gere um documento diferente do esperado.

O template descreve o documento. Os dados ficam em um JSON separado, a fonte é registrada explicitamente e o formato final é escolhido no comando de export.

1. Crie o menor documento possível​

Salve como report.yml:

filemaker: "1.0"
model: document
id: my-first-report
page:
preset: A4
elements:
- id: title
type: text
text: "Hello, FileMaker"
x: 20mm
y: 20mm
width: 170mm
height: 12mm
style:
font: NotoSans
font_size: 22pt
color: "#17324d"
  • filemaker seleciona o contrato. Em V1, use exatamente "1.0".
  • model escolhe document, canvas ou dataset.
  • id é o identificador estável do template.
  • page define a área física.
  • elements contém os itens visuais em ordem estável.
  • Cada texto precisa de uma fonte registrada; fontes do sistema não são descobertas automaticamente.

Valide antes de renderizar:

appcore-filemaker check report.yml --json
appcore-filemaker validate report.yml \
--font NotoSans=./NotoSans-Regular.ttf \
--json
appcore-filemaker render report.yml \
--font NotoSans=./NotoSans-Regular.ttf \
--format pdf \
--output report.pdf

check valida o schema. validate também resolve fonte e layout. render gera o artefato. Para PDF de produção, execute ainda preflight --strict.

2. Escolha página, orientação e áreas de segurança​

page:
preset: A4
orientation: portrait
margin: { top: 12mm, right: 12mm, bottom: 15mm, left: 12mm }
safe: { top: 8mm, right: 8mm, bottom: 8mm, left: 8mm }
bleed: { top: 3mm, right: 3mm, bottom: 3mm, left: 3mm }
crop_marks: false
  • preset usa um tamanho conhecido. Há séries A0..A10, B0..B10, C0..C10, tamanhos norte-americanos, tela, foto, envelope, etiqueta, térmico e social.
  • orientation aceita portrait ou landscape.
  • margin reserva a margem de layout.
  • safe marca a área segura dentro da página.
  • bleed declara sangria fora do corte.
  • crop_marks solicita marcas de corte quando o exporter as suporta.
  • Em vez de preset, use width e height; não use os dois modos juntos.

Exemplo de canvas sem preset:

page:
width: 1920px
height: 1080px

3. Entenda as unidades​

Todo comprimento precisa de unidade.

FormaUso
12pttipografia e medidas PostScript
20pxcanvas e telas; 96 DPI lógico
15mm, 2.5cm, 1inmedidas físicas
50%metade da dimensão do container
0.5normforma normalizada de 50%
12luunidade lógica fornecida pelo contexto
automedição automática apenas nos campos que a aceitam

Não escreva x: 20. Escreva x: 20mm, 20pt ou outra unidade explícita.

4. Crie tokens, tema e estilos reutilizáveis​

themes:
base:
tokens:
ink: "#17324d"
accent: "#2e75b6"
pale: "#eef4f8"
style:
color: "$ink"
board:
extends: base
theme: board

styles:
title:
font: NotoSans
font_size: 24pt
color: "$ink"
card:
fill: "$pale"
stroke: "$accent"
stroke_width: 1pt
  • themes declara temas nomeados.
  • extends herda outro tema e não pode formar ciclos.
  • tokens guarda cores ou outros valores usados como $nome.
  • theme ativa exatamente um tema.
  • style no topo aplica um estilo a todo o template.
  • styles cria estilos nomeados.
  • styles: [title, card] num elemento aplica estilos na ordem declarada.
  • style no elemento é a última camada estática daquele elemento.

Campos de style:

CampoPara que serve
fillpreenchimento
strokecontorno
stroke_widthespessura do contorno
opacityopacidade de 0 a 1000000
fontnome lógico da fonte registrada
font_sizetamanho da fonte
colorcor do texto

Cores aceitam nomes estáveis, hexadecimal, rgb(...), rgba(...), gray(...), cmyk(...) em milionésimos e valores tipados. Opacidade 240000 significa 24%.

5. Adicione texto com comportamento de overflow​

- id: summary
type: text
text: "A bounded operational summary"
x: 20mm
y: 40mm
width: 100mm
height: 18mm
styles: [title]
text_options:
overflow: shrink
max_lines: 2
min_font_size: 10pt
line_height: 1200000
writing_mode: horizontal

overflow aceita:

  • wrap: quebra linhas;
  • shrink: reduz até min_font_size;
  • ellipsis: termina com reticências;
  • clip: recorta e registra diagnóstico;
  • expand: aumenta a caixa medida;
  • error: falha se não couber.

Acréscimos beta 5 (publicados em 2026-10-01)​

Os acréscimos abaixo estão disponíveis em appcore-filemaker 0.1.0-beta.5. São recursos genéricos de layout; cada aplicação fornece seus dados tipados e rótulos de domínio.

Elementos de texto e colunas de tabela podem declarar align_x: start|center|end; text_options.padding_inline aplica recuo simétrico a cada linha moldada. O alinhamento usa a largura final após wrap e shrink. Campos YAML desconhecidos continuam causando erro de validação. Blocos de texto independentes aceitam text_options.padding: { top, right, bottom, left }; insets absolutos, lógicos ou percentuais abaixo de 50% reduzem o retângulo medido e são aplicados por todos os exporters visuais. Percentuais horizontais usam a largura do elemento; topo e base usam sua altura. A paginação de texto expandido reserva padding vertical em cada fragmento. Cada coluna de tabela também pode declarar padding: { top, right, bottom, left } com comprimentos absolutos ou lógicos não negativos. O retângulo interno resolvido é usado na medição, paginação e desenho do texto em todos os exporters; colunas auto incluem os insets horizontais na largura medida. Regras condicionais de tabela aceitam text_offset_y para deslocar verticalmente o texto desenhado sem alterar conteúdo medido, altura das linhas ou paginação. Use comprimento absoluto ou lógico não zero, menor que o tamanho efetivo da fonte e a altura interna da célula. O texto continua recortado aos limites originais; PDF, SVG, raster e HTML aplicam o mesmo deslocamento. text_offset_y_first_page e text_offset_y_continuation podem sobrescrevê-lo conforme o papel físico da página.

Composições de uma linha com estilo compartilhado podem usar text_segments em vez de text ou do binding do elemento:

data_schema:
amount: { type: string }
elements:
- id: total
type: text
x: 10mm
y: 20mm
width: 180mm
height: 8mm
text_segments:
- { text: 'TOTAL:', gap_after: 7pt }
- { binding: data.amount }
text_options: { overflow: error, max_lines: 1, align_x: end }

Cada segmento exige exatamente um text literal ou binding resolvido como string. Os segmentos compartilham o estilo do elemento, usam escrita horizontal e uma linha e não quebram separadamente; gaps aceitam comprimentos absolutos ou lógicos não negativos. Estilos mistos, composição BiDi e conteúdo multilinha usam elementos de fluxo separados.

Texto horizontal com overflow: expand em fluxo vertical é dividido somente entre linhas moldadas completas. Uma linha maior que a área útil da página falha explicitamente. Para manter elementos irmãos relacionados, marque keep_with_next: true até o penúltimo elemento do fluxo vertical. Linhas de tabela podem usar keep_together_by: layout_group: chaves iguais e contíguas ficam juntas quando couberem; grupos maiores se dividem entre linhas completas. Cada linha individual ainda precisa caber em uma página. Represente um bloco lógico maior que a página em várias linhas componentes com a mesma chave; valores incluídos nos totais devem aparecer em apenas uma delas para evitar agregação duplicada. Estilos condicionais também aceitam line_height como razão em milionésimos de 500000 a 4000000. O template pode ajustar o entrelinha por estilo de linha; por exemplo, style: { line_height: 1250000 } significa 1,25. É um controle tipográfico genérico, sem política de layout da aplicação.

writing_mode aceita horizontal (padrão) ou vertical. No modo vertical, o texto quebra pelo limite de altura, cada coluna é moldada de cima para baixo e as colunas avançam da direita para a esquerda. PDF, SVG, PNG/JPEG e HTML usam as mesmas colunas e runs resolvidos, sem reflow no exporter; PDF e raster usam os avanços dos glyphs diretamente.

6. Desenhe formas vetoriais​

- { id: surface, type: rect, x: 20mm, y: 70mm, width: 70mm, height: 35mm, style: { fill: "#eef4f8" } }
- { id: status, type: circle, x: 25mm, y: 76mm, width: 20mm, height: 20mm, style: { fill: "#2a9d8f" } }
- { id: divider, type: line, x: 20mm, y: 112mm, width: 170mm, height: 0pt, style: { stroke: "#2e75b6", stroke_width: 1pt } }

Tipos implementados:

typeUso específico
texttexto medido com fonte explícita
imageimagem resolvida por asset explícito
linelinha simples ou comandos de path
rectretângulo
circlecírculo; largura e altura devem ser iguais
ellipseelipse
polygonpolígono fechado sem curvas
pathpath com linhas e curvas Bézier cúbicas
groupcontainer de filhos
tabletabela tipada e paginável

chart permanece reservado. A fonte ainda não publicada suporta EAN-8/EAN-13 com type: barcode e QR Code UTF-8 com type: qr, validados e convertidos em paths vetoriais no layout. Consulte o guia para limites de payload e paths. Para gráficos, componha rect, line, path e text.

Um path semântico:

- id: trend
type: path
x: 105mm
y: 70mm
width: 80mm
height: 35mm
style: { stroke: "#f4a261", stroke_width: 2pt }
path:
- { command: move, x: 0%, y: 80% }
- { command: line, x: 30%, y: 55% }
- { command: curve, x1: 45%, y1: 70%, x2: 70%, y2: 10%, x: 100%, y: 20% }

Comandos aceitos: move, line, curve e close. curve usa x1, y1, x2, y2, x, y.

7. Separe os dados do layout​

Declare o schema no YAML:

data_schema:
report_title: { type: string }
requests: { type: integer }
target_met: { type: boolean }
rows: { type: array }
above_target:
type: boolean
computed: 'data.target_met == true'

Tipos: string, integer, decimal, boolean, date, date_time, duration, currency, array, object e null. Use nullable: true quando o campo aceitar nulo. computed é uma expressão determinística, sem rede ou IO.

No elemento, associe o valor:

- id: bound-title
type: text
binding: data.report_title
x: 20mm
y: 20mm
width: 170mm
height: 12mm
styles: [title]

Controle visibilidade e repetição:

- id: success-badge
type: rect
when: data.target_met
x: 160mm
y: 20mm
width: 30mm
height: 10mm

- id: repeated-row
type: group
repeat: data.rows
children: []
  • binding fornece o conteúdo principal do elemento.
  • when pinta o elemento somente quando a expressão for verdadeira.
  • repeat expande o elemento uma vez por item de um array, sob limites.
  • style_rules aplica estilos condicionais em ordem.
style_rules:
- when: 'data.target_met == true'
style: { fill: "#e4f4ec", color: "#1f7a4d" }

8. Organize elementos em grupos e flows​

- id: kpis
type: group
x: 20mm
y: 55mm
width: 170mm
height: 35mm
layout: flow_horizontal
distribute: space_between
gap: 5mm
children:
- { id: kpi-a, type: rect, width: 50mm, height: 30mm }
- { id: kpi-b, type: rect, width: 50mm, height: 30mm }
- { id: kpi-c, type: rect, width: 50mm, height: 30mm }

layout aceita absolute, flow_vertical ou flow_horizontal. distribute aceita start, center, end, space_between, space_around ou space_evenly. Distribuição diferente de start exige tamanhos explícitos ou preferidos nos filhos. gap é a distância normal entre filhos.

9. Crie uma tabela paginável​

- id: results
type: table
binding: data.rows
x: 15mm
y: 120mm
width: 180mm
height: 150mm
style: { font: NotoSans, font_size: 8pt, color: "#17324d" }
table:
columns:
- { field: name, header: Name, width: { mode: flex, value: 2 } }
- { field: amount, header: Amount, width: { mode: fixed, value: 30mm } }
- { field: status, header: Status, width: { mode: auto } }
repeat_header: true
group_by: region
total_fields: [amount]
conditional_styles:
- { when: 'data.status == "Watch"', style: { fill: "#fff2e8", color: "#8f4b17" } }
auto_sample_rows: 8
max_rows: 100
max_row_fields: 12
max_cell_bytes: 256
header_height: 8mm
row_height: 7mm
  • columns define ordem, field, header e largura.
  • Largura fixed é exata, flex divide o espaço restante e auto mede uma amostra limitada.
  • repeat_header repete o cabeçalho após paginação.
  • group_by preserva a chave de agrupamento.
  • total_fields soma campos numéricos exatos somente na última página.
  • conditional_styles estiliza uma linha usando seu objeto como data.
  • auto_sample_rows limita a amostra de colunas auto.
  • max_rows, max_row_fields e max_cell_bytes só podem apertar os limites globais, nunca aumentá-los.
  • row_height: auto mede cada linha; uma medida explícita é mais previsível.

Regras condicionais também aceitam padding_first_page e padding_continuation para sobrescrever o padding da linha conforme a página de destino; quando ausente, usa-se o padding comum da regra. São controles de layout declarativos e genéricos.

Os acréscimos YAML beta5 incluem também row_anchor_field para âncoras de linha que acompanham sua página física, page_bodies.first e page_bodies.continuation para regiões distintas de tabela e min_height condicional com overrides para primeira página e continuação. Esses recursos não introduzem cálculos ou layout específicos de uma aplicação no Runtime.

Limitações conhecidas nesta revisão do código-fonte: cada linha/célula de tabela precisa caber em uma página; blocos lógicos maiores só fluem quando são compostos por linhas completas ou elementos de fluxo vertical. Runs de rich text não são suportados; elementos separados em fluxo são a alternativa de composição. A fonte ainda não publicada suporta EAN-8/EAN-13 e QR Code pelo layout; outras simbologias não são inferidas. As fontes devem ser fornecidas e selecionadas explicitamente; o Runtime não escolhe silenciosamente fontes do host. Cada aplicação mantém seus dados de domínio e desenho do documento em YAML; as primitivas genéricas não codificam regras comerciais de uma aplicação específica.

10. Adicione cabeçalho, rodapé, watermark e numeração​

page:
preset: A4
master:
header:
- { id: top-rail, type: rect, x: 0mm, y: 0mm, width: 100%, height: 7mm, style: { fill: "#17324d" }, locked: true }
footer:
- { id: page-number, type: text, text: "Page {page} of {pages}", x: 160mm, y: 285mm, width: 35mm, height: 5mm, style: { font: NotoSans, font_size: 8pt }, locked: true }
first:
footer:
- id: confidential
type: text
text: "CONFIDENTIAL"
x: 5mm
y: 113mm
width: 200mm
height: 42mm
style: { font: NotoSans, font_size: 78pt, color: "#8da3b5", opacity: 240000 }
transform: { rotate: -32, origin_x: 50%, origin_y: 50% }
locked: true

Cada papel (master, first, continuation, last) aceita background, header e footer.

  • master aparece em todas as páginas.
  • first aparece na primeira.
  • continuation aparece apenas nas páginas do meio.
  • last aparece na última quando há mais de uma página.
  • {page} e {pages} são resolvidos depois da paginação.
  • Layers de página não participam da colisão do corpo.
  • locked: true impede patches runtime naquele elemento.

11. Use imagens explicitamente​

- id: hero
type: image
asset: images/hero.png
x: 20mm
y: 40mm
width: 170mm
height: 70mm
image:
fit: cover
focal_x: 500000
focal_y: 350000
crop: { left: 0, top: 0, right: 0, bottom: 0 }
respect_exif: true

fit aceita contain, cover, fill, none e scale_down. Foco e crop usam partes por milhão entre 0 e 1000000. O runner deve fornecer o mesmo resolver de assets ao layout e ao export. Caminhos são resolvidos numa raiz canônica limitada; traversal e symlinks de escape são rejeitados.

12. Posicione com guides, regions, anchors e constraints​

guides:
content-left: 15mm

regions:
body: { x: 15mm, y: 25mm, width: 180mm, height: 245mm }

elements:
- id: card
type: rect
region: body
anchors: { left: "guide:content-left" }
constraints:
min_width: 40mm
preferred_width: 60mm
max_width: 90mm
preferred_height: 30mm
aspect_ratio: 2000000
align_y: center
  • guides são coordenadas nomeadas.
  • regions são containers retangulares nomeados e podem ter collision.
  • region escolhe o container do elemento.
  • anchors conecta uma borda a guide ou elemento anterior, com offset opcional.
  • constraints aceita min_width, preferred_width, max_width, min_height, preferred_height, max_height e aspect_ratio em milionésimos.
  • align_x/align_y aceitam start, center, end; não combine alinhamento e coordenada explícita no mesmo eixo.

13. Transforme sem alterar o conteúdo​

transform:
translate_x: 2mm
translate_y: 0mm
rotate: -15
scale_x: 1000000
scale_y: 1000000
flip_x: false
flip_y: false
mirror: none
origin_x: 50%
origin_y: 50%

Escalas usam milionésimos. mirror aceita none, horizontal, vertical ou both. O transform é resolvido antes da colisão e é consumido igualmente por todos os exporters gráficos.

14. Controle colisão e áreas proibidas​

Forma simples:

collision: false

Forma completa:

collision:
enabled: true
group: content
collides_with: [content, exclusion]
ignore: [decorative-line]
priority: 10
movable: true
bounds: layout
policy: push

bounds seleciona o bound de colisão suportado pelo contrato. policy aceita push, error, overlay, next_page ou shrink. A herança é documento → página → region → group → elemento.

Reserve uma área que não é pintada:

exclusions:
header-clearance:
x: 0mm
y: 0mm
width: 100%
height: 18mm
group: exclusion
collides_with: [content]

15. Reutilize components e includes​

components:
metric-card:
props:
label: Metric
slots:
detail: []
elements:
- { id: surface, type: rect, x: 0mm, y: 0mm, width: 50mm, height: 25mm }

elements:
- id: requests-card
type: group
component: metric-card
props: { label: Requests }

components declara props, slots substituíveis e elementos. Uma instância usa component, props e slots. IDs expandidos recebem namespace seguro.

includes:
- path: fragments/header.yml
namespace: report

includes são resolvidos por um resolver sandboxed. namespace evita colisão de IDs. Includes não podem assumir o controle das layers físicas da página.

16. Declare intenção para o bridge de IA​

ai:
purpose: "Maintain a bounded monthly report."
rules:
- "Preserve page numbering."
- "Never remove the confidential watermark."
editable: [report-title, results]
locked: [confidential, page-number]

O core determinístico apenas preserva essa política. Somente o bridge opcional appcore-filemaker-ai a interpreta. purpose descreve o objetivo, rules impõe regras textuais, editable lista IDs editáveis e locked lista IDs que o bridge não pode alterar.

Referência completa do topo do YAML​

CampoObrigatórioFunção
filemakersimversão, exatamente "1.0"
modelsimdocument, canvas ou dataset
idsimID lógico estável
pagenãogeometria, margens e layers físicas
collisionnãopolítica herdada de colisão
includesnãofragments resolvidos explicitamente
componentsnãocomponentes reutilizáveis
themesnãotemas, herança, tokens e style
themenãotema ativo
stylenãostyle global do template
stylesnãostyles nomeados
guidesnãoposições nomeadas
regionsnãocontainers retangulares nomeados
exclusionsnãoáreas não pintadas reservadas
data_schemanãocontrato de dados tipados
elementsnãoelementos raiz em ordem estável
ainãointenção para bridges externos

Referência completa de um elemento​

CampoFunção
idID único de 1–128 caracteres ASCII seguros
typetipo do elemento
componentcomponente instanciado
propspropriedades da instância
slotsconteúdo de slots nomeados
x, y, width, heightgeometria com unidade
constraintsmínimos, preferidos, máximos e aspect ratio
align_x, align_yalinhamento no container
texttexto literal
text_optionsoverflow, linhas, tamanho mínimo e escrita
tableconfiguração obrigatória de type: table
assetreferência explícita de imagem
imagefit, foco, crop e EXIF
pathcomandos vetoriais
styleslista de styles nomeados
stylestyle inline
style_rulesstyles condicionais ordenados
transformtranslação, rotação, escala e espelhamento
layoutabsolute ou flow
distributedistribuição dos filhos do flow
gapdistância entre filhos
bindingexpressão do dado principal
whencondição de visibilidade
repeatexpressão de array para repetição
anchorsanchors nomeados
regionregion contendo o elemento
childrenfilhos do elemento/group
lockedbloqueia patches runtime
hiddeninicia oculto
layernome da layer visual
z_indexordem dentro da layer
collisionoverride de colisão do elemento

Nem todo campo serve para todo tipo. text_options vale em text; tabelas aceitam somente min_font_size, line_height e writing_mode, pois o planner da tabela controla overflow e limites de linhas das células. table e um binding de array são obrigatórios em table; comandos path só são válidos em line, path ou polygon; uma tabela não aceita children ou slots.

Ordem recomendada de trabalho​

  1. Comece com filemaker, model, id, page e um texto.
  2. Execute check.
  3. Registre a fonte e execute validate.
  4. Adicione data_schema e o JSON tipado separado.
  5. Crie tokens/styles antes de repetir style inline.
  6. Adicione grupos e tabelas em blocos pequenos.
  7. Use inspect, explain e debug quando o layout surpreender.
  8. Execute preflight --strict para o formato final.
  9. Renderize somente depois que o preflight estiver limpo.

Use appcore-filemaker schema --json e appcore-filemaker capabilities --json como referência executável do binário instalado. Veja também o exemplo básico e o exemplo intermediário.