Bands
Um relatório é dividido em faixas horizontais chamadas bands. Cada band tem uma altura fixa e contém elementos posicionados por coordenadas x e y.
┌──────────────────────────────────────┐
│ Header Band (repete em toda página) │
├──────────────────────────────────────┤
│ Detail Band (uma por registro) │
│ Detail Band (uma por registro) │
│ Detail Band (uma por registro) │
│ ... │
├──────────────────────────────────────┤
│ Summary Band (uma vez, após dados) │
├──────────────────────────────────────┤
│ Footer Band (repete em toda página) │
└──────────────────────────────────────┘Tipos
Header
Aparece no topo de cada página do PDF. Usado para títulos, logos e informações fixas.
{
"type": "header",
"id": "header_main",
"height": 100,
"elements": [ ... ]
}Detail
Repete uma vez para cada registro do data source vinculado. É aqui que os dados tabulares são renderizados.
{
"type": "detail",
"id": "detail_items",
"dataSourceId": "items",
"height": 30,
"elements": [ ... ]
}O dataSourceId indica qual data source alimenta esta band. Cada registro gera uma instância da band no documento final.
Summary
Aparece uma vez após todos os registros de detalhe. Usado para totais e agregados.
{
"type": "summary",
"id": "summary_totals",
"height": 50,
"elements": [ ... ]
}Footer
Aparece no rodapé de cada página. Usado para paginação e informações legais.
{
"type": "footer",
"id": "footer_page",
"height": 30,
"elements": [ ... ]
}Propriedades comuns
| Propriedade | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | string | Sim | "header", "detail", "summary", "footer", "groupHeader" ou "groupFooter" |
id | string | Sim | Identificador único |
height | double | Sim | Altura em pontos |
elements | array | Sim | Lista de elementos posicionados |
dataSourceId | string | detail, groupHeader | Data source que alimenta a band |
groupBy | string | groupHeader, groupFooter | Campo do data source usado para agrupar registros |
forcePageBreakBefore | bool | Não | Se true, insere quebra de página antes desta band |
visibility | string | Não | Controla em quais páginas a band é renderizada (header/footer) |
showOnPage | int | Não | Renderiza somente na página informada (1-based). Sobrepõe visibility |
printWhen | string | Não | Expressão booleana — band é ocultada quando avalia false ou 0 |
Visibilidade por página (Header e Footer)
As bands header e footer aceitam o campo visibility para controlar em quais páginas são renderizadas. Isso é útil para criar cabeçalhos de capa, rodapés de assinatura ou layouts que diferem por página.
| Valor | Comportamento |
|---|---|
"allPages" (padrão) | Renderizada em todas as páginas |
"firstPageOnly" | Somente na primeira página |
"lastPageOnly" | Somente na última página |
"allButFirst" | Em todas exceto a primeira |
"allButLast" | Em todas exceto a última |
"oddPagesOnly" | Somente páginas ímpares (1, 3, 5…) |
"evenPagesOnly" | Somente páginas pares (2, 4, 6…) |
"none" | Nunca renderizada (band oculta) |
{
"type": "header",
"id": "header_capa",
"height": 120,
"visibility": "firstPageOnly",
"elements": [ ... ]
}{
"type": "footer",
"id": "footer_assinatura",
"height": 60,
"visibility": "lastPageOnly",
"elements": [ ... ]
}showOnPage
Renderiza a band somente em uma página específica (1-based). Quando definido, sobrepõe visibility:
{
"type": "header",
"id": "header_pagina3",
"height": 40,
"showOnPage": 3,
"elements": [ ... ]
}Renderização condicional (printWhen)
A propriedade printWhen aceita uma expressão booleana avaliada em tempo de renderização. A band é ocultada quando a expressão avalia false ou 0. Usa o mesmo ExpressionEvaluator dos field bindings.
{
"type": "summary",
"id": "summary_totais",
"height": 50,
"printWhen": "total > 0",
"elements": [ ... ]
}Outros exemplos:
"printWhen": "pageNumber == 1"
"printWhen": "itemCount > 10"
"printWhen": "status == 'ativo'"CSV e Excel
Em exportações CSV/Excel (sem conceito de página), qualquer valor de visibility diferente de "none" inclui a band. printWhen é avaliado normalmente.
Posicionamento
Elementos dentro de uma band usam coordenadas relativas ao canto superior esquerdo da band:
Band (height: 40)
┌───────────────────────────────┐
│ ┌─────┐ │ Elemento em x:10, y:5
│ │ Txt │ │
│ └─────┘ │
│ ┌────┐ │ Elemento em x:300, y:15
│ │ Fld│ │
│ └────┘ │
└───────────────────────────────┘A coordenada y é relativa ao topo da band, não da página.
Múltiplas bands do mesmo tipo
Um relatório pode ter mais de uma band de cada tipo. A ordem no array bands define a ordem de renderização:
{
"bands": [
{ "type": "header", "id": "h1", "height": 80, "elements": [...] },
{ "type": "header", "id": "h2", "height": 40, "elements": [...] },
{ "type": "detail", "id": "d1", "dataSourceId": "items", "height": 30, "elements": [...] },
{ "type": "summary", "id": "s1", "height": 50, "elements": [...] },
{ "type": "footer", "id": "f1", "height": 25, "elements": [...] }
]
}Quebra de página forçada
{
"type": "summary",
"id": "sum",
"height": 60,
"forcePageBreakBefore": true,
"elements": [ ... ]
}O summary acima sempre começa em uma nova página.
Group Bands (quebra de grupo)
Relatórios gerenciais frequentemente exigem agrupamento — por vendedor, por cliente, por período. O Sulfite suporta isso com dois tipos de band: groupHeader e groupFooter.
┌──────────────────────────────────────┐
│ Header Band │
├──────────────────────────────────────┤
│ ┌── GroupHeader (vendedor: "Ana") ──┐
│ │ Detail Band (venda 1) │
│ │ Detail Band (venda 2) │
│ └── GroupFooter (subtotal Ana) ─────┘
│ ┌── GroupHeader (vendedor: "Carlos")┐
│ │ Detail Band (venda 3) │
│ └── GroupFooter (subtotal Carlos) ──┘
├──────────────────────────────────────┤
│ Summary Band (total geral) │
├──────────────────────────────────────┤
│ Footer Band │
└──────────────────────────────────────┘GroupHeader
Renderiza uma vez no início de cada grupo. O campo groupBy indica qual campo do data source define a quebra.
{
"type": "groupHeader",
"id": "gh_vendedor",
"dataSourceId": "vendas",
"groupBy": "vendedor",
"height": 35,
"elements": [
{ "type": "field", "id": "f_vendedor", "x": 10, "y": 8, "width": 300, "binding": "vendedor", "bold": true, "fontSize": 14 }
]
}GroupFooter
Renderiza uma vez no final de cada grupo. Ideal para subtotais. O AggregateElement dentro de um groupFooter calcula o agregado apenas sobre os registros do grupo.
{
"type": "groupFooter",
"id": "gf_vendedor",
"groupBy": "vendedor",
"height": 30,
"elements": [
{ "type": "aggregate", "id": "subtotal", "x": 300, "y": 5, "width": 100, "verb": "SUM", "dataSourceId": "vendas", "targetKey": "valor", "format": "currency:BRL", "bold": true }
]
}Exemplo completo com agrupamento
{
"bands": [
{ "type": "header", "id": "hdr", "height": 60, "elements": [...] },
{ "type": "groupHeader", "id": "gh", "dataSourceId": "vendas", "groupBy": "vendedor", "height": 35, "elements": [...] },
{ "type": "detail", "id": "det", "dataSourceId": "vendas", "height": 25, "elements": [...] },
{ "type": "groupFooter", "id": "gf", "groupBy": "vendedor", "height": 30, "elements": [...] },
{ "type": "summary", "id": "sum", "height": 40, "elements": [...] },
{ "type": "footer", "id": "ftr", "height": 30, "elements": [...] }
]
}O DataProcessor agrupa os registros automaticamente pela chave groupBy e renderiza a hierarquia: groupHeader → detail rows → groupFooter para cada grupo.
printWhen em GroupHeader
Se o groupHeader tem printWhen que avalia false, o grupo inteiro (header + details + footer) é suprimido.
Exportação multiformato
GroupBands funcionam em todos os formatos: PDF, HTML, CSV e Excel. Cada renderer respeita a hierarquia de grupo e o printWhen.
Editando bands no Studio
O Sulfite Studio oferece dois pontos de edição de bands sem precisar editar JSON manualmente:
- Painel lateral — expanda uma band no painel esquerdo do designer para editar
height,dataSourceId,forcePageBreakBefore,visibility,showOnPageeprintWhendiretamente. - BandManagerDialog — aberto pela toolbar (botão Gerenciar Bands), exibe todas as bands em cards expansíveis com todos os campos editáveis, drag & drop para reordenar e botão de exclusão com confirmação.
Todas as edições feitas no Studio são refletidas imediatamente no editor JSON (modo Code) e vice-versa.