Enhance PDF report generation with custom font support and update Dockerfile for font installation
- Added support for custom PDF fonts in ReportService, allowing dynamic font resolution based on configuration. - Introduced a method to handle font candidates and ensure fallback options for PDF generation. - Updated Dockerfile to install the Nanum font, improving PDF rendering for Korean text. - Modified application.yml to include a configurable path for the PDF font.
This commit is contained in:
parent
eb7df53eff
commit
8224cd02f5
|
|
@ -10,8 +10,11 @@ RUN gradle --no-daemon bootJar
|
|||
FROM eclipse-temurin:21-jre
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends fonts-nanum \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /workspace/build/libs/*.jar /app/app.jar
|
||||
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import com.hanwha.nexacrodemo.consolidation.ConsolidationPayload;
|
|||
import com.hanwha.nexacrodemo.minio.ObjectStorageService;
|
||||
import com.hanwha.nexacrodemo.minio.StoredObject;
|
||||
import com.lowagie.text.Document;
|
||||
import com.lowagie.text.DocumentException;
|
||||
import com.lowagie.text.Element;
|
||||
import com.lowagie.text.Font;
|
||||
import com.lowagie.text.FontFactory;
|
||||
import com.lowagie.text.PageSize;
|
||||
import com.lowagie.text.Paragraph;
|
||||
import com.lowagie.text.Phrase;
|
||||
import com.lowagie.text.Rectangle;
|
||||
import com.lowagie.text.pdf.BaseFont;
|
||||
import com.lowagie.text.pdf.PdfPCell;
|
||||
import com.lowagie.text.pdf.PdfPTable;
|
||||
import com.lowagie.text.pdf.PdfWriter;
|
||||
|
|
@ -22,10 +23,13 @@ import java.awt.Color;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.DecimalFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -43,6 +47,7 @@ import org.springframework.core.io.ByteArrayResource;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
|
|
@ -55,15 +60,19 @@ public class ReportService {
|
|||
private final ReportMapper reportMapper;
|
||||
private final ConsolidationMapper consolidationMapper;
|
||||
private final ObjectStorageService objectStorageService;
|
||||
private final String pdfFontPath;
|
||||
private volatile BaseFont pdfBaseFont;
|
||||
|
||||
public ReportService(
|
||||
ReportMapper reportMapper,
|
||||
ConsolidationMapper consolidationMapper,
|
||||
ObjectStorageService objectStorageService
|
||||
ObjectStorageService objectStorageService,
|
||||
@Value("${app.report.pdf-font-path:}") String pdfFontPath
|
||||
) {
|
||||
this.reportMapper = reportMapper;
|
||||
this.consolidationMapper = consolidationMapper;
|
||||
this.objectStorageService = objectStorageService;
|
||||
this.pdfFontPath = pdfFontPath;
|
||||
}
|
||||
|
||||
public TxResponse loadRunOverview() {
|
||||
|
|
@ -358,7 +367,7 @@ public class ReportService {
|
|||
}
|
||||
|
||||
private void addPdfTitle(Document document, String text, int size, Color color) {
|
||||
Font font = FontFactory.getFont(FontFactory.HELVETICA_BOLD, size, color);
|
||||
Font font = pdfFont(size, Font.BOLD, color);
|
||||
Paragraph paragraph = new Paragraph(text, font);
|
||||
paragraph.setAlignment(Element.ALIGN_LEFT);
|
||||
paragraph.setSpacingAfter(4f);
|
||||
|
|
@ -380,14 +389,14 @@ public class ReportService {
|
|||
}
|
||||
|
||||
private void addDetailTable(Document document, String title, List<Map<String, Object>> rows, int maxRows) {
|
||||
Font sectionFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 12, new Color(24, 54, 93));
|
||||
Font sectionFont = pdfFont(12, Font.BOLD, new Color(24, 54, 93));
|
||||
Paragraph section = new Paragraph(title, sectionFont);
|
||||
section.setSpacingBefore(8f);
|
||||
section.setSpacingAfter(6f);
|
||||
document.add(section);
|
||||
|
||||
if (rows.isEmpty()) {
|
||||
document.add(new Paragraph("데이터가 없습니다.", FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 10, new Color(120, 120, 120))));
|
||||
document.add(new Paragraph("데이터가 없습니다.", pdfFont(10, Font.ITALIC, new Color(120, 120, 120))));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -412,13 +421,13 @@ public class ReportService {
|
|||
if (rows.size() > maxRows) {
|
||||
document.add(new Paragraph(
|
||||
"전체 " + rows.size() + "건 중 상위 " + maxRows + "건만 표시합니다.",
|
||||
FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 9, new Color(120, 120, 120))
|
||||
pdfFont(9, Font.ITALIC, new Color(120, 120, 120))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private PdfPCell pdfHeaderCell(String text) {
|
||||
PdfPCell cell = new PdfPCell(new Phrase(text, FontFactory.getFont(FontFactory.HELVETICA_BOLD, 9, Color.WHITE)));
|
||||
PdfPCell cell = new PdfPCell(new Phrase(text, pdfFont(9, Font.BOLD, Color.WHITE)));
|
||||
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
cell.setBackgroundColor(new Color(243, 127, 32));
|
||||
|
|
@ -428,7 +437,7 @@ public class ReportService {
|
|||
}
|
||||
|
||||
private PdfPCell pdfBodyCell(String text, int alignment) {
|
||||
PdfPCell cell = new PdfPCell(new Phrase(text, FontFactory.getFont(FontFactory.HELVETICA, 9, Color.DARK_GRAY)));
|
||||
PdfPCell cell = new PdfPCell(new Phrase(text, pdfFont(9, Font.NORMAL, Color.DARK_GRAY)));
|
||||
cell.setHorizontalAlignment(alignment);
|
||||
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
cell.setBorderColor(new Color(226, 229, 233));
|
||||
|
|
@ -436,6 +445,53 @@ public class ReportService {
|
|||
return cell;
|
||||
}
|
||||
|
||||
private Font pdfFont(float size, int style, Color color) {
|
||||
BaseFont baseFont = resolvePdfBaseFont();
|
||||
if (baseFont == null) {
|
||||
return new Font(Font.HELVETICA, size, style, color);
|
||||
}
|
||||
return new Font(baseFont, size, style, color);
|
||||
}
|
||||
|
||||
private BaseFont resolvePdfBaseFont() {
|
||||
if (pdfBaseFont != null) {
|
||||
return pdfBaseFont;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (pdfBaseFont != null) {
|
||||
return pdfBaseFont;
|
||||
}
|
||||
for (String candidate : pdfFontCandidates()) {
|
||||
try {
|
||||
pdfBaseFont = BaseFont.createFont(candidate, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
|
||||
return pdfBaseFont;
|
||||
} catch (DocumentException | IOException ignored) {
|
||||
// Fall through to the next candidate. The Docker image installs NanumGothic,
|
||||
// but local test environments may not have a Korean font available.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> pdfFontCandidates() {
|
||||
List<String> candidates = new ArrayList<>();
|
||||
if (pdfFontPath != null && !pdfFontPath.isBlank()) {
|
||||
candidates.add(pdfFontPath.trim());
|
||||
}
|
||||
addIfExists(candidates, "/usr/share/fonts/truetype/nanum/NanumGothic.ttf");
|
||||
addIfExists(candidates, "/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf");
|
||||
addIfExists(candidates, "/workspace/server/api/src/main/resources/fonts/NanumGothic.ttf");
|
||||
addIfExists(candidates, "/app/resources/fonts/NanumGothic.ttf");
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void addIfExists(List<String> candidates, String path) {
|
||||
if (Files.exists(Path.of(path))) {
|
||||
candidates.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNumericValue(Object value) {
|
||||
return value instanceof Number || value instanceof BigDecimal;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ app:
|
|||
enabled: false
|
||||
poll-interval-ms: ${BATCH_POLL_INTERVAL_MS:5000}
|
||||
worker-name: ${BATCH_WORKER_NAME:batch-1}
|
||||
report:
|
||||
pdf-font-path: ${PDF_FONT_PATH:}
|
||||
storage:
|
||||
provider: ${STORAGE_PROVIDER:minio}
|
||||
bucket: ${MINIO_BUCKET:reports}
|
||||
|
|
@ -57,4 +59,3 @@ logging:
|
|||
level:
|
||||
root: INFO
|
||||
com.hanwha.nexacrodemo: INFO
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue