- Substituição de placeholders de texto.
- Substituição de placeholders em loop.
- Substituição de imagens, incluindo substituição por marcadores e placeholders personalizados.
- Gráficos básicos: linhas, pizza e barras.
Ambiante de Execução:
- Aspose Word versão 21.1.
- JDK 18.
Abaixo estão os exemplos de código.
Dependências Maven
import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.StandardChartTheme; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.chart.renderer.category.StandardBarPainter; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.DefaultPieDataset;
import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import java.text.NumberFormat;
public class UtilidadeGrafico { private static final Color[] CORES_BARRAS = new Color[]{ new Color(79, 129, 189), new Color(192, 80, 77), new Color(155, 187, 89), };
private static final Color[] CORES_LINHAS = new Color[]{
new Color(237, 123, 46),
new Color(90, 154, 213),
new Color(164, 164, 164),
new Color(255, 87, 34),
new Color(76, 175, 80),
new Color(33, 150, 243),
new Color(156, 39, 176),
new Color(255, 193, 7),
new Color(0, 188, 212),
new Color(233, 30, 99),
new Color(0, 150, 136),
new Color(255, 152, 0),
};
private static final Shape[] FORMAS = new Shape[]{
new Ellipse2D.Double(-5, -5, 10, 10),
new Rectangle2D.Double(-5, -5, 10, 10),
new Polygon(new int[]{0, 10, 0, -10}, new int[]{-10, 0, 10, 0}, 4),
new Polygon(new int[]{0, 10, -10}, new int[]{10, -10, -10}, 3),
new Polygon(new int[]{0, 10, -10, 0}, new int[]{0, 10, 10, 0}, 4)
};
private static final Color[] CORES_PIZZA = new Color[]{
new Color(75, 172, 198),
new Color(128, 100, 162),
new Color(155, 187, 89),
new Color(192, 80, 77),
new Color(79, 129, 189),
new Color(44, 77, 117),
new Color(247, 150, 70),
new Color(165, 165, 165),
};
private static StandardChartTheme inicializarTemaGrafico() {
StandardChartTheme temaAtual = new StandardChartTheme("JFree");
temaAtual.setLargeFont(new java.awt.Font("宋体", java.awt.Font.BOLD, 15));
temaAtual.setRegularFont(new java.awt.Font("宋体", java.awt.Font.PLAIN, 13));
temaAtual.setExtraLargeFont(new java.awt.Font("宋体", java.awt.Font.BOLD, 20));
temaAtual.setPlotBackgroundPaint(new Color(255, 255, 204, 0));
temaAtual.setPlotOutlinePaint(new Color(0, 0, 0, 0));
temaAtual.setRangeGridlinePaint(new Color(78, 74, 74));
return temaAtual;
}
public static JFreeChart graficoLinha(String titulo, String rotuloEixoCategoria, String rotuloEixoValor, DefaultCategoryDataset conjuntoDados) {
ChartFactory.setChartTheme(inicializarTemaGrafico());
JFreeChart grafico = ChartFactory.createLineChart(
titulo,
rotuloEixoCategoria,
rotuloEixoValor,
conjuntoDados,
PlotOrientation.VERTICAL,
true,
true,
false
);
CategoryPlot trama = grafico.getCategoryPlot();
LineAndShapeRenderer renderizador = (LineAndShapeRenderer) trama.getRenderer();
renderizador.setBaseItemLabelsVisible(true);
renderizador.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
for (int i = 0; i < conjuntoDados.getRowKeys().size(); i++) {
renderizador.setSeriesPaint(i, CORES_LINHAS[i]);
renderizador.setSeriesShapesVisible(i, true);
renderizador.setSeriesShape(i, FORMAS[i]);
}
NumberAxis eixoY = (NumberAxis) trama.getRangeAxis();
eixoY.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(50));
return grafico;
}
public static JFreeChart graficoBarra(String titulo, String rotuloEixoCategoria, String rotuloEixoValor, DefaultCategoryDataset conjuntoDados) {
ChartFactory.setChartTheme(inicializarTemaGrafico());
JFreeChart grafico = ChartFactory.createBarChart(
titulo,
rotuloEixoCategoria,
rotuloEixoValor,
conjuntoDados,
PlotOrientation.VERTICAL,
true,
true,
false
);
CategoryPlot trama = grafico.getCategoryPlot();
BarRenderer renderizador = (BarRenderer) trama.getRenderer();
renderizador.setBarPainter(new StandardBarPainter());
renderizador.setBaseItemLabelsVisible(true);
renderizador.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderizador.setItemMargin(0.0);
for (int i = 0; i < conjuntoDados.getRowKeys().size(); i++) {
renderizador.setSeriesPaint(i, CORES_BARRAS[i]);
}
return grafico;
}
public static JFreeChart graficoPizza(String titulo, DefaultPieDataset conjuntoDados) {
ChartFactory.setChartTheme(inicializarTemaGrafico());
JFreeChart grafico = ChartFactory.createPieChart(
titulo,
conjuntoDados,
true,
true,
false
);
PiePlot trama = (PiePlot) grafico.getPlot();
for (int i = 0; i < conjuntoDados.getKeys().size(); i++) {
trama.setSectionPaint(conjuntoDados.getKey(i), CORES_PIZZA[i]);
}
trama.setBaseSectionOutlinePaint(new Color(255, 255, 255));
trama.setBaseSectionOutlineStroke(new BasicStroke(3));
trama.setLabelLinkPaint(new Color(255, 255, 255, 0));
trama.setLabelBackgroundPaint(new Color(255, 255, 255, 0));
trama.setLabelOutlinePaint(new Color(255, 255, 255, 0));
trama.setLabelShadowPaint(new Color(255, 255, 255, 0));
trama.setShadowPaint(new Color(255, 255, 255, 0));
trama.setLabelGenerator(new StandardPieSectionLabelGenerator(("{0}{2}"), NumberFormat.getNumberInstance(), new DecimalFormat("0.00%")));
return grafico;
}
}
Classe de Utilitários para Gráficos
</div><div>```
package com.example.demo;
import com.aspose.words.*;
import org.jfree.chart.ChartUtilities;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class UtilitariosAspose {
public static void gerarPdfPorTemplateWord(String template, String caminhoSaida, String dadosJson, String nomeFonte, List<ImagemDocumento> imagensDocumento) throws Exception {
Assert.isTrue(StringUtils.hasLength(template), "O caminho do arquivo de template não pode estar vazio!");
Assert.isTrue(StringUtils.hasLength(nomeFonte), "O nome da fonte de dados do template é obrigatório!");
Assert.isTrue(StringUtils.hasLength(caminhoSaida), "O caminho de saída do PDF não pode estar vazio!");
Document doc = new Document(template);
DocumentBuilder construtor = new DocumentBuilder(doc);
JsonDataLoadOptions opcoes = new JsonDataLoadOptions();
JsonDataSource fonteDadosJson = new JsonDataSource(new ByteArrayInputStream(dadosJson.getBytes()), opcoes);
final ReportingEngine motor = new ReportingEngine();
motor.buildReport(doc, fonteDadosJson, nomeFonte);
if (!CollectionUtils.isEmpty(imagensDocumento)) {
List<ImagemDocumento> imagensMarcador = imagensDocumento.stream().filter(p -> p.getTipoFusao() == 1).collect(Collectors.toList());
inserirImagemMarcador(imagensMarcador, construtor);
List<ImagemDocumento> imagensDinamicas = imagensDocumento.stream().filter(p -> p.getTipoFusao() == 2).collect(Collectors.toList());
inserirImagemDinamica(imagensDinamicas, doc, construtor);
}
doc.save(caminhoSaida, SaveFormat.PDF);
}
private static void inserirImagemDinamica(List<ImagemDocumento> imagensDocumento, Document doc, DocumentBuilder construtor) throws Exception {
NodeCollection paragrafos = doc.getChildNodes(NodeType.PARAGRAPH, true);
for (Object no : paragrafos) {
Paragraph paragrafo = (Paragraph) no;
String placeholderAntigo = paragrafo.toString(SaveFormat.TEXT);
String placeholderNovo = placeholderAntigo
.replaceAll("[\n\r]", "")
.replaceAll("[{}]", "");
if (paragrafo.toString(SaveFormat.TEXT).contains("imagem_placeholder")) {
for (Run run : paragrafo.getRuns()) {
if (run.getText().contains("imagem_placeholder")) {
String[] palavrasChave = placeholderNovo.split("_");
if (palavrasChave.length != 3) {
continue;
}
String chave = palavrasChave[2];
ImagemDocumento imagemDoc = imagensDocumento.stream().filter(p -> p.getChaveDinamica().equals(chave)).findFirst().orElse(null);
if (imagemDoc == null) {
continue;
}
construtor.moveTo(run);
inserirImagem(imagemDoc, construtor);
}
run.setText("");
}
}
}
}
private static void inserirImagemMarcador(List<ImagemDocumento> imagensMarcador, DocumentBuilder construtor) throws Exception {
for (ImagemDocumento imagemDoc : imagensMarcador) {
if (!StringUtils.hasLength(imagemDoc.getMarcador())) {
continue;
}
construtor.moveToBookmark(imagemDoc.getMarcador());
inserirImagem(imagemDoc, construtor);
}
}
private static void inserirImagem(ImagemDocumento imagemDoc, DocumentBuilder construtor) throws Exception {
if (imagemDoc.getTipo() == 1 || imagemDoc.getTipo() == 3) {
construtor.insertImage(imagemDoc.getCaminho());
} else if (imagemDoc.getTipo() == 2) {
construtor.insertImage(imagemDoc.getBytesImagem());
}
}
public static void main(String[] args) throws Exception {
final String caminhoArquivo = "C:\\Users\\Administrator\\Desktop\\upload\\pasta_nova\\modelo1.docx";
final String caminhoSaida = "C:\\Users\\Administrator\\Desktop\\upload\\pasta_nova\\saida.pdf";
String anotacoes = "Notas\n" +
"Raio-X torácico (sem película)\n" +
"Local de exame\n" +
"Projetos de sangue (segundo andar, centro de teste)\n" +
"Eletrocardiograma (última sala à esquerda na área médica ocidental)\n" +
"Raio-X torácico (ao lado do elevador)\n" +
"Pressão arterial (departamento de emissão)\n" +
"Ultrassom (última sala à esquerda na área médica ocidental)";
String jsonStr = "{\n" +
" \"idade\": 20,\n" +
" \"nascimento\": \"1999-01-55\",\n" +
" \"numeroSerial\": \"12349864\",\n" +
" \"numeroCartao\": \"56456461645\",\n" +
" \"itens\": [\n" +
" {\n" +
" \"verificado\": false,\n" +
" \"nomeItem\": \"Hemograma completo\"\n" +
" },\n" +
" {\n" +
" \"verificado\": false,\n" +
" \"nomeItem\": \"Teste bioquímico\"\n" +
" },\n" +
" {\n" +
" \"verificado\": true,\n" +
" \"nomeItem\": \"Análise de urina\"\n" +
" }\n" +
" ],\n" +
" \"nome\": \"Zhang San San\",\n" +
" \"telefone\": \"15445454454\",\n" +
" \"sexo\": \"Masculino\",\n" +
" \"nomeHospital\": \"Centro de Saúde Comunitário de Jinma, Wenjiang, Chengdu\",\n" +
" \"dataExame\": \"\\u200B2024-12-13\",\n" +
" \"horaImpressao\": \"\\u200B2024-12-13 14:50:22\",\n" +
" \"nomeMedico\": \"Dr. Li\",\n" +
" \"descricao\": \"" + anotacoes + "\",\n" +
" \"nomePacote\": \"Pacote 1\"\n" +
"}";
DefaultCategoryDataset conjuntoLinha = new DefaultCategoryDataset();
conjuntoLinha.addValue(150, "Alta direita", "2002");
conjuntoLinha.addValue(140, "Alta direita", "2003");
conjuntoLinha.addValue(150, "Alta direita", "2004");
conjuntoLinha.addValue(95, "Baixa direita", "2002");
conjuntoLinha.addValue(95, "Baixa direita", "2003");
conjuntoLinha.addValue(95, "Baixa direita", "2004");
conjuntoLinha.addValue(120, "Alta esquerda", "2002");
conjuntoLinha.addValue(130, "Alta esquerda", "2003");
conjuntoLinha.addValue(160, "Alta esquerda", "2004");
conjuntoLinha.addValue(90, "Baixa esquerda", "2002");
conjuntoLinha.addValue(88, "Baixa esquerda", "2003");
conjuntoLinha.addValue(95, "Baixa esquerda", "2004");
ByteArrayOutputStream fluxoSaida = new ByteArrayOutputStream();
ChartUtilities.writeChartAsJPEG(fluxoSaida, UtilidadeGrafico.graficoLinha("Gráfico de Linha de Teste", "A", "B", conjuntoLinha), 500, 500);
List<ImagemDocumento> imagensDocumento = new ArrayList<>();
byte[] bytesImagem = fluxoSaida.toByteArray();
ImagemDocumento imgDoc = ImagemDocumento.builder()
.marcador("linha")
.tipo(2)
.bytesImagem(bytesImagem)
.build();
imagensDocumento.add(imgDoc);
ImagemDocumento imgDoc1 = ImagemDocumento.builder()
.marcador("codigoQr")
.tipo(2)
.bytesImagem(ZxingUtil.gerarCodigoQr("12349864", 100, 100))
.build();
imagensDocumento.add(imgDoc1);
gerarPdfPorTemplateWord(caminhoArquivo, caminhoSaida, jsonStr, "dados", imagensDocumento);
}
}
Classe de Utilitários para Processamento de Documentos
import lombok.Builder; import lombok.Data;
@Data @Builder public class ImagemDocumento {
private int tipoFusao;
private String chaveDinamica;
private String marcador;
private int tipo;
private String caminho;
private byte[] bytesImagem;
}
Dados da Imagem do Documento
</div><div>```
void testeGerarPdf() throws Exception {
String dadosStr = "{\n" +
" \"nomeHospital\": \"Resultados do Exame\",\n" +
" \"itensExame\": [\n" +
" {\n" +
" \"nomeItem\": \"Hemograma completo\",\n" +
" \"tipoItem\": 1,\n" +
" \"resultadoItem\": [\n" +
" {\n" +
" \"nomeItem\": \"Alanina aminotransferase (ALT)\",\n" +
" \"resultado\": \"44.0\",\n" +
" \"unidade\": \"U/L\",\n" +
" \"intervaloReferencia\": \"0-42\",\n" +
" \"dica\": \"↑\",\n" +
" \"idResultado\": \"1\"\n" +
" },\n" +
" {\n" +
" \"nomeItem\": \"Aspartato aminotransferase (AST)\",\n" +
" \"resultado\": \"39.0\",\n" +
" \"unidade\": \"U/L\",\n" +
" \"intervaloReferencia\": \"0-42\",\n" +
" \"dica\": \"\",\n" +
" \"idResultado\": \"2\"\n" +
" },\n" +
" {\n" +
" \"nomeItem\": \"Bilirrubina total (T-BIL)\",\n" +
" \"resultado\": \"11.8\",\n" +
" \"unidade\": \"μmol/L\",\n" +
" \"intervaloReferencia\": \"3.5-22\",\n" +
" \"dica\": \"\"\n" +
" },\n" +
" {\n" +
" \"nomeItem\": \"Ureia no sangue (REA)\",\n" +
" \"resultado\": \"9.89\",\n" +
" \"unidade\": \"mmol/L\",\n" +
" \"intervaloReferencia\": \"1.7-8.3\",\n" +
" \"dica\": \"\",\n" +
" \"idResultado\": \"3\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"nomeItem\": \"Ultrassom abdominal\",\n" +
" \"tipoItem\": 2,\n" +
" \"resultadoItem\": [\n" +
" {\n" +
" \"resultado\": \"https://cloud.leenleda.com:3005/jinma/examination/checkFile/2412031010431774/20241203105828723.jpg\",\n" +
" \"idResultado\": \"4\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}";
final String caminhoArquivo = "C:\\Users\\Administrator\\Desktop\\upload\\pasta_nova\\resultado_exame.docx";
final String caminhoSaida = "C:\\Users\\Administrator\\Desktop\\upload\\pasta_nova\\saida.pdf";
List<ImagemDocumento> imagensDocumento = new ArrayList<>();
ImagemDocumento imgDoc = ImagemDocumento.builder()
.tipo(3)
.caminho("https://img2.selfimg.com.cn/CNTgalleryLowerrightWatermark/2017/06/19/1497858683_cEh3WY.jpg")
.chaveDinamica("4")
.tipoFusao(2)
.build();
imagensDocumento.add(imgDoc);
UtilitariosAspose.gerarPdfPorTemplateWord(caminhoArquivo, caminhoSaida, dadosStr, "dados", imagensDocumento);
}
Código de Teste
- Geralmente, use tabelas para o layout do template Word.
- Consulte a documentação oficial para placeholders e sintaxe do template, ou veja exemplos.
- Para imagens, use marcadores como placeholders.