Este guia descreve o fluxo manual (sem Studio). Objetivo: ter uma pasta *Report com HTML Vash + controller.js, servida pela sua app Node, gerando PDF por um dos engines deste pacote.
src/core/
lotes/
dicon/
termo/
cienciaReport/
controller.js
report.html
report_header.html (opcional)
report_footer.html (opcional)
crystal-pdf.json (opcional: margens/página)
images/ (opcional)
Regras:
Report (ex.: cienciaReport)..../cienciaReport → /lotes/dicon/termo/ciencia.controller.js (module.exports = async function (req, res) { ... }).npm install @crystal-node/runtime
Escolha um engine e instale as deps dele na sua app:
| Engine | Comando / extras | Links |
|---|---|---|
| Devbox | (só o runtime; precisa de res.pdf) |
— |
| Puppeteer | npm install puppeteer vash |
https://www.npmjs.com/package/puppeteer · https://www.npmjs.com/package/vash |
| wkhtmltopdf | npm install vash + binário no SO |
https://www.npmjs.com/package/vash · https://wkhtmltopdf.org/downloads.html |
| WeasyPrint | Python 3 + pip install weasyprint (+ Pango) |
https://doc.courtbouillon.org/weasyprint/ |
Para rotas automáticas com Express:
npm install express
https://www.npmjs.com/package/express
Detalhes: engines-pdf.md.
Exemplo com Puppeteer (app Node genérica):
const {
reportControllerDotnetrequest,
crystalFunctionsRuntime,
reportEnginePuppeteer
} = require('@crystal-node/runtime');
module.exports = async function (req, res) {
const model = reportControllerDotnetrequest.buildReportModel(
req,
__dirname,
crystalFunctionsRuntime
);
await reportEnginePuppeteer.renderReportPdf(model, res, {
reportDir: __dirname,
pdf: {
marginTop: 28,
marginBottom: 14,
marginLeft: 0,
marginRight: 0,
pageWidth: '215.9mm',
pageHeight: '279.4mm'
}
});
};
Em Devbox, troque só o engine:
const { reportEngineDevbox } = require('@crystal-node/runtime');
await reportEngineDevbox.renderReportPdf(model, res, { reportDir: __dirname, pdf: { ... } });
Com wkhtmltopdf:
const { reportEngineWkhtmltopdf } = require('@crystal-node/runtime');
await reportEngineWkhtmltopdf.renderReportPdf(model, res, { reportDir: __dirname, pdf: { ... } });
report-controller-dotnetrequest-runtimereport-controller-runtimeNo topo de report.html (e header/footer, se existirem):
@{
var crystalFunctions = (model && model.__crTemplateRuntime)
? model.__crTemplateRuntime
: null;
if (!crystalFunctions) {
throw new Error("Crystal functions runtime nao inicializado no model.");
}
}
Helpers frequentes: rows, joinRows, getField, toText, iif — ver README do pacote e o código de crystal-functions-runtime.
const path = require('path');
const express = require('express');
const { mountReportRoutes } = require('@crystal-node/runtime');
const app = express();
app.use(express.json({ limit: '20mb' }));
mountReportRoutes(app, {
rootDir: path.join(__dirname, 'src/core'),
basePath: '/',
method: 'post'
});
Rota do exemplo: POST /lotes/dicon/termo/ciencia
API: rotas-report.md.
nomeDoRelatorioReport no caminho desejado.report.html (+ header/footer se necessário).images/.controller.js com controller + engine escolhido.mountReportRoutes (ou rota manual).subreportDatasourceKeys) se preciso..rpt / não gera HTML a partir do Crystal (isso é o gerador/Studio).