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'"
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.
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.