ย ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์กฐ์ฌ
Java๋ฅผ ์ด์ฉํด์ PDF๋ฅผ ๊ทธ๋ฆด ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ํ์ ์ผ๋ก iText์ PDFBox๊ฐ ์กด์ฌํฉ๋๋ค.
์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชจ๋ PDF๋ฅผ ๊ทธ๋ฆฌ๊ธฐ์ ์ถฉ๋ถํ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง ๋ผ์ด์ผ์ค์์์ ์ฝ๊ฐ์ ์ฐจ์ด๋ฅผ ๊ฐ๊ณ ์์ต๋๋ค.
iText๋ AGPL ๋ผ์ด์ผ์ค๋ฅผ ๊ฐ๊ณ ์์ผ๋ฉฐ, AGPL ๋ผ์ด์ผ์ค๋ GNU Affero General Public License์ ์ฝ์๋ก, ์์ ์ํํธ์จ์ด ์ฌ๋จ(FSF)์์ ๋ง๋ ์คํ ์์ค ๋ผ์ด์ผ์ค์
๋๋ค. ์ด ๋ผ์ด์ผ์ค๋ ์ฌ์ฉ์๊ฐ ํ๋ก๊ทธ๋จ์ ์คํํ๋ ๊ฒ๋ฟ๋ง ์๋๋ผ ์น ์๋น์ค๋ฅผ ํตํด ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ ๋์๋ ํด๋น ํ๋ก๊ทธ๋จ์ ์์ค ์ฝ๋๋ฅผ ๊ณต๊ฐํด์ผ ํจ์ ์๊ตฌํฉ๋๋ค.
Apache PDFBox๋ Apache ๋ผ์ด์ผ์ค 2.0์ ๊ฐ๊ณ ์์ผ๋ฉฐ, Apache ๋ผ์ด์ผ์ค 2.0์ด๋ ์คํ ์์ค ์ํํธ์จ์ด ๋ผ์ด์ผ์ค ์ค ํ๋๋ก, ์ํ์น ์ํํธ์จ์ด ์ฌ๋จ(Apache Software Foundation)์์ ๊ฐ๋ฐํ ๋ผ์ด์ผ์ค์
๋๋ค. ์ด ๋ผ์ด์ผ์ค๋ ์์
์ ๋ฐ ๋น์์
์ ๋ชฉ์ ์ผ๋ก ์ํํธ์จ์ด๋ฅผ ์์ ๋กญ๊ฒ ์ฌ์ฉํ๊ณ ์์ ํ ์ ์๋๋ก ํ์ฉํฉ๋๋ค.
์ ๋ ์ด ๊ธฐ๋ฅ์ ํ๋ก๋์
๋ ๋ฒจ์ ์ฌ์ฉํ ์์ ์ด๋ฏ๋ก Apache PDFBox๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ์ต๋๋ค.
ย ๋์์ธ
๋์์ด๋๋ถ๊ป PDF ํ
ํ๋ฆฟ์ ๋ํ ๋์์ธ์ ๋ฐ์ ํ ๊ฐ๋ฐ์ ์์ํ์์ต๋๋ค.
ย ๊ฐ๋ฐ
build.gradle ์์กด์ฑ ์ค์
//Apache PDFBox
implementation 'org.apache.pdfbox:pdfbox:2.0.29'
Java
๋ณต์ฌ
Maven Repository์ ๊ฒ์ ํ Vulnerabilities ์ Usages๋ฅผ ํ์ธํ์ฌ ๊ฐ์ฅ ์ต์ ๋ฒ์ ์ด๋ฉด์ ์์ ์ ์ธ ๋ฒ์ ์ ์ฐพ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํด์ค๋๋ค.
Controller
@GetMapping(value = "/{resumeId}/pdf", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<ByteArrayResource> createPDF(HttpServletRequest request, @PathVariable Long resumeId) {
PdfDTO pdf = resumeService.createResumePDF(request, resumeId);
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=" + URLEncoder.encode(pdf.getFileName(), StandardCharsets.UTF_8));
return new ResponseEntity<>(pdf.getPdf(), headers, HttpStatus.OK);
}
Java
๋ณต์ฌ
ํด๋น API์ Response์ ๋ํ Content-Type์ application/pdf๋ก ์ค์ ํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ์์์ ๋ณด๋ผ ๋ ์ด ์์์ด pdf file ํ์
์ด๋ผ๋ ๊ฒ์ ๋ช
์ํด์ค๋๋ค.
๋ํ Content-Disposition๋ฅผ ํตํด ์์ฑ๋ PDF์ ํ์ผ ์ด๋ฆ์ ๋ช
์ํด์ฃผ๋๋ฐ ์ด ์์ฑ์ default ๊ฐ์ inline;์ผ๋ก ์ด๊ฑธ attachment;๋ก ๋ณ๊ฒฝํ๋ฉด PDF ํ์ผ์ ๋ค์ด๋ฐ๊ฒ ํ ์ ์์ต๋๋ค.
์ด ๋ ํ์ผ ์ด๋ฆ์ UTF-8๋ก URLEncodeํด์ฃผ์ง ์์ผ๋ฉด ํ๊ธ์ ๊ฒฝ์ฐ ํ์๊ฐ ๋์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
DTO
@Getter @Setter
@NoArgsConstructor
public class PdfDTO {
private String fileName;
private ByteArrayResource pdf;
@Builder
public PdfDTO(String fileName, ByteArrayResource pdf) {
this.fileName = fileName;
this.pdf = pdf;
}
}
Java
๋ณต์ฌ
Service ๋จ์์ ์์ฑํ PDF byte[]์ ํด๋น ํ์ผ์ ์ด๋ฆ์ ํจ๊ป ๋ด์ Controller๋จ๊น์ง ์ ๋ฌํ๊ธฐ ์ํ DTO๋ฅผ ์ ์ํด์ค๋๋ค.
Service
public PdfDTO createResumePDF(HttpServletRequest request, Long resumeId) {
String accessToken = authenticationProvider.resolveAccessToken(request);
Long userId = authenticationProvider.getId(accessToken);
User user = userRepository.findById(userId).orElseThrow(() -> new CustomException(ErrorCode.BLANK_MEMBER));
Resume resume = resumeRepository.findResumeById(resumeId).orElseThrow(() -> new CustomException(ErrorCode.BLANK_RESUME));
if (!resume.getUser().getId().equals(user.getId())) {
throw new CustomException(ErrorCode.ACCESS_DENIED);
}
if (resume.getUser().getUserProfile() == null && (resume.getEducations() == null || resume.getEducations().isEmpty())) {
throw new CustomException(ErrorCode.BLANK_REQUIRED);
}
byte[] bytes;
try {
PDDocument document = pdfUtil.createPDFDocument();
pdfUtil.setDocumentInfo(document, resume);
pdfUtil.initFonts(document);
int height = (int) (document.getPage(0).getMediaBox().getHeight() - 84);
height = pdfUtil.setProfile(document, resume, height);
height = pdfUtil.setEducations(document, resume, height);
height = pdfUtil.setCareers(document, resume, height);
height = pdfUtil.setCertifications(document, resume, height);
height = pdfUtil.setFluencies(document, resume, height);
height = pdfUtil.setActivities(document, resume, height);
pdfUtil.setPageNumber(document);
pdfUtil.setWaterMark(document);
bytes = pdfUtil.pdfToBytes(document);
document.close();
} catch (IOException e) {
throw new CustomException(ErrorCode.IO_ERROR);
}
return PdfDTO.builder()
.fileName("[๊ณ ์ด๋์กธ๋ท์ปด_์ด๋ ฅ์]_" + resume.getUser().getUserProfile().getName() + "_" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".pdf")
.pdf(new ByteArrayResource(bytes))
.build();
}
Java
๋ณต์ฌ
Service๋จ์์ ๋จผ์ ํด๋น ์ ์ ์ ์ด๋ ฅ์๊ฐ ๋ง๋์ง ๊ทธ๋ฆฌ๊ณ ์ด๋ ฅ์๋ฅผ ๋ง๋ค๊ธฐ ์ํ ํ์ ํญ๋ชฉ๋ค์ด ๋ค ์กด์ฌํ๋์ง๋ฅผ ๊ฒ์ฆํฉ๋๋ค.
์ดํ PDF๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์ํ ๋ฉ์๋๋ค์ ์ ์ ํด๋์ Util ํด๋์ค๋ฅผ ์ฃผ์
๋ฐ์ ๋น PDF ๋ฌธ์๋ฅผ ์์ฑํ ํ ๊ฐ ํญ๋ชฉ๋ค์ ๊ทธ๋ ค์ค ํ DTO์ ๋ด์ return ํด์ค๋๋ค.
PDFUtil.createPDFDocument()
public PDDocument createPDFDocument() {
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
return document;
}
Java
๋ณต์ฌ
๋น PDF ๋ฌธ์๋ฅผ ์์ฑํ ํ ํด๋น ๋ฌธ์์ A4 ํฌ๊ธฐ์ ๋น ํ์ด์ง๋ฅผ ์ถ๊ฐํ์ฌ ๋ฆฌํด ํด์ค๋๋ค.
PDFUtil.createPDFPage()
private PDPage createPDFPage() {
PDPage page = new PDPage(PDRectangle.A4);
return page;
}
Java
๋ณต์ฌ
PDF๋ฅผ ๊ทธ๋ฆฌ๋ค๊ฐ ํ์ด์ง๊ฐ ๊ฝ ์ฐฐ ๊ฒฝ์ฐ ์๋์ผ๋ก ํ์ด์ง๊ฐ ์ถ๊ฐ๋์ง ์๊ธฐ ๋๋ฌธ์ ์๋ก์ด ํ์ด์ง๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
PDFUtil.needNewPage()
private boolean needNewPage(PDDocument document, int height, int needHeight) {
if (height - needHeight < 84) {
PDPage newPage = createPDFPage();
document.addPage(newPage);
return true;
}
return false;
}
Java
๋ณต์ฌ
๋ฌธ์๋ ๋ผ์ธ์ ์ถ๊ฐํ๋ ์ค ํ์ํ ๋์ด๊ฐ ๊ธฐ๋ณธ ํ์ด์ง ๊ฐ๊ฒฉ์ ๋์ด๊ฐ๋์ง๋ฅผ ํ์ธํด ์๋ก์ด ํ์ด์ง๋ฅผ ์ถ๊ฐํด์ค๋๋ค.
PDFUtil.initFonts()
public void initFonts(PDDocument document) throws IOException {
Map<String, PDType0Font> map = new HashMap<>();
ClassPathResource extraBoldFontPath = new ClassPathResource("fonts/Pretendard-ExtraBold.ttf");
ClassPathResource mediumFontPath = new ClassPathResource("fonts/Pretendard-Medium.ttf");
ClassPathResource regularFontPath = new ClassPathResource("fonts/Pretendard-Regular.ttf");
map.put("extraBold", PDType0Font.load(document, extraBoldFontPath.getInputStream()));
map.put("medium", PDType0Font.load(document, mediumFontPath.getInputStream()));
map.put("regular", PDType0Font.load(document, regularFontPath.getInputStream()));
fonts = map;
}
Java
๋ณต์ฌ
๋ฌธ์๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์ํ ํฐํธ ํ์ผ์ ์ฝ์ด๋ค์ฌ ์ฌ์ฉํ๊ธฐ ํธํ๋๋ก Map์ ๋ด์ ๋ค๋ฅธ ๋ฉ์๋์์๋ ์ฌ์ฉํ ์ ์๋๋ก ์ด๊ธฐํ ํด์ค๋๋ค.
์ด ๋ ์ค์ํ ์ ์ resources ๊ฒฝ๋ก์ ์๋ ํ์ผ๋ค์ ๋ก์ปฌ๊ณผ ์๋ฒ์์ ๊ฐ๊ฐ ๋ค๋ฅธ ๊ฒฝ๋ก๋ฅผ ๊ฐ์ง ์ ์๊ธฐ ๋๋ฌธ์ ClassPathResource ๊ฐ์ ํด๋์ค๋ฅผ ์ด์ฉํด ์ ๋๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ค๋๋ค.
PDFUtil.setText()
private int setText(PDDocument document, PDType0Font font, int fontSize, int r, int g, int b, int x, int y, String text, int maxLength) throws IOException {
PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(document.getPages().getCount() - 1), PDPageContentStream.AppendMode.APPEND, true);
if (needNewPage(document, y, 22)) {
contentStream.close();
contentStream = new PDPageContentStream(document, document.getPage(document.getPages().getCount() - 1), PDPageContentStream.AppendMode.APPEND, true);
y = (int) (document.getPage(document.getPages().getCount() - 1).getMediaBox().getHeight() - 84);
}
contentStream.setFont(font, fontSize);
contentStream.setNonStrokingColor(r, g, b);
contentStream.beginText();
contentStream.newLineAtOffset(x, y);
if (maxLength != 0 && text.length() > maxLength) {
for (int i = 0; i < text.length(); i += maxLength) {
int endIndex = Math.min(i + maxLength, text.length());
contentStream.showText(text.substring(i, endIndex));
contentStream.newLineAtOffset(0, -18);
y = i != maxLength ? y - 18 : y;
}
} else {
contentStream.showText(text);
}
contentStream.endText();
contentStream.close();
return y;
}
Java
๋ณต์ฌ
PDF์ ๋ค์ด๊ฐ๋ ์์๋ค์ ๊ทธ๋ฆฌ๊ธฐ ์ํ PDPageContentStream์ ๋ง๋ค์ด์ฃผ์ด์ผํ๋๋ฐ ๊ทธ๋ฆด ๊ณณ์ ๋ฌธ์์ ํ์ด์ง๋ฅผ ์๋ ค์ฃผ์ด์ผํฉ๋๋ค.
๊ทธ๋ฆฌ๊ธฐ ์ ์ needNewPage๋ฅผ ์ด์ฉํด ํ์ฌ ์์น์์ ๋ฌธ์๋ฅผ ์ถ๊ฐํ์ ๋ ๊ธฐ๋ณธ ํ์ด์ง ๊ฐ๊ฒฉ์ ๋์ด๊ฐ๋์ง ํ์ธํ์ฌ ์๋ก์ด ํ์ด์ง๋ก ์ ํํ๊ฑฐ๋ ํ์ฌ ํ์ด์ง๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํฉ๋๋ค.
stream์ ์ฌ์ฉํ ํฐํธ์ ์, ์์น ๋ฑ์ ์ง์ ํ ํ ์
๋ ฅํ ๋ฌธ์๋ฅผ showText()์ ๋๊ฒจ์ค ๊ทธ๋ฆฌ๊ฒ๋ ํฉ๋๋ค.
์ด ๋ PDF์ x์ถ์ผ๋ก ๊ธ์๊ฐ ๋์น ์ ์์ผ๋ฏ๋ก ์ต๋ ๊ธ์๋ฅผ ๋๋๋ค๋ฉด ์ค๋ฐ๊ฟ์ ํด์ฃผ๋ ๋ก์ง์ ์ถ๊ฐํด์ค๋๋ค.
stream์ newLineAtOffset() ๋ฉ์๋๋ beginText()๋ฅผ ํ ํ ์ฒซ ์์น๋ ์ ๋์ขํ๋ก ๊ทธ ์ดํ ์์น๋ endText()๊ฐ ํธ์ถ๋๊ธฐ ์ ๊น์ง ์๋์ขํ๋ก ์ฌ์ฉํฉ๋๋ค.
PDFUtil.setImage()
private void setImage(PDDocument document, int x, int y, int width, int height, BufferedImage bufferedImage) throws IOException {
PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(document.getPages().getCount() - 1), PDPageContentStream.AppendMode.APPEND, true);
if (needNewPage(document, y, height)) {
contentStream.close();
contentStream = new PDPageContentStream(document, document.getPage(document.getPages().getCount() - 1), PDPageContentStream.AppendMode.APPEND, true);
y = (int) (document.getPage(document.getPages().getCount() - 1).getMediaBox().getHeight() - 84);
}
PDImageXObject image = LosslessFactory.createFromImage(document, bufferedImage);
contentStream.drawImage(image, x, y, width, height);
contentStream.close();
}
Java
๋ณต์ฌ
์๋ถ๋ถ์์ ๋๊ฐ์ด Stream์ ๋ง๋ ํ ํ์ด์ง ์ ํ์ ํ์ธํฉ๋๋ค.
์ด๋ฏธ์ง ์ถ๊ฐ๋ ๊ฐ๋จํ๊ฒ BufferedImage๋ก ImageObject๋ฅผ ๋ง๋ ํ ๊ทธ๋ฆด x, y ์ขํ์ ์ด๋ฏธ์ง์ ํฌ๊ธฐ๋ฅผ ์ง์ ํ์ฌ ๊ทธ๋ ค์ค๋๋ค.
PDFUtil.getImageByURL()
private BufferedImage getImageByURL(String imagePath) throws IOException {
InputStream inputStream = new ByteArrayInputStream(s3Util.getObject(imagePath.substring(imagePath.indexOf("/", 8) + 1)));;
BufferedImage image = ImageIO.read(inputStream);
inputStream.close();
return image;
}
Java
๋ณต์ฌ
์ ํฌ ์๋น์ค์์๋ ์ด๋ ฅ์์ ํ๋กํ ์ด๋ฏธ์ง๋ฅผ S3์ ์ ์ฅํ ํ CDN URL์ ํตํด Deliveryํ๊ณ ์๊ธฐ ๋๋ฌธ์ URL์ key๋ฅผ ํตํด S3์์ ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
PDFUtil.getImageByResource()
private BufferedImage getImageByResource(ClassPathResource resource) throws IOException {
InputStream inputStream = resource.getInputStream();
BufferedImage image = ImageIO.read(inputStream);
inputStream.close();
return image;
}
Java
๋ณต์ฌ
ํ๋กํ ์ด๋ฏธ์ง๋ฟ๋ง ์๋๋ผ ์ํฐ๋งํฌ ๋ฑ ์ด๋ ฅ์์์ ๊ณตํต์ผ๋ก ์ฌ์ฉ๋๋ ์ด๋ฏธ์ง๋ค์ /resources/images์ ๊ฐ๊ณ ์๋ค๊ฐ ์ ๋๊ฒฝ๋ก๋ฅผ ํตํด ๋ถ๋ฌ์์ ์ฌ์ฉํฉ๋๋ค.
PDFUtil.setLine()
private int setHorizontalLine(PDDocument document, int lineWidth, int r, int g, int b, int startX, int startY, int endX, int endY) throws IOException {
PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(document.getPages().getCount() - 1), PDPageContentStream.AppendMode.APPEND, true);
if (needNewPage(document, startY, 1)) {
contentStream.close();
contentStream = new PDPageContentStream(document, document.getPage(document.getPages().getCount() - 1), PDPageContentStream.AppendMode.APPEND, true);
startY = (int) (document.getPage(document.getPages().getCount() - 1).getMediaBox().getHeight() - 84);
endY = (int) (document.getPage(document.getPages().getCount() - 1).getMediaBox().getHeight() - 84);
}
contentStream.setLineWidth(lineWidth);
contentStream.setStrokingColor(r, g, b);
contentStream.moveTo(startX, startY);
contentStream.lineTo(endX, endY);
contentStream.stroke();
contentStream.close();
return startY - 34;
}
Java
๋ณต์ฌ
Stream์ stroke()๋ฅผ ํตํด ๋ผ์ธ์ ๊ทธ๋ฆด ์ ์๋๋ฐ ์ ๊ตต๊ธฐ, ์, ์์์์น, ์ข
๋ฃ์์น๋ฅผ ์ค์ ํ์ฌ ์ ์ ๊ทธ์ด์ค๋๋ค.
PDFUtil.calculatePixel()
private int calculatePixel(String fontName, int fontSize, String text) throws IOException {
PDType0Font font = fonts.get(fontName);
float pixel = font.getStringWidth(text);
return (int) ((pixel / 1000 * fontSize) + 8);
}
Java
๋ณต์ฌ
ํฐํธ๋ง๋ค width์ ๋ํ ํฝ์
์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๊ฐ๊ฒฉ์ ์กฐ์ ํ๋ ค๋ฉด ๋ฌธ์์ ํฝ์
์ ๊ณ์ฐํด์ฃผ์ด์ผํฉ๋๋ค.
PDFBox์ Fontํ์
์์ ์ ๊ณตํด์ฃผ๋ getStringWidth()๋ฅผ ํตํด ๊ฐ๋ก ๊ธธ์ด๋ฅผ ๊ตฌํ ํ pixcel / 1000 * fontSize ๋ก ํด๋น ๋ฌธ์์ ๊ฐ๋ก ๊ธธ์ด ํฝ์
์ ๊ตฌํด์ค๋๋ค.
๋ง์ง๋ง์ 8์ ๋ํด์ฃผ์ด ๋์ด์ฐ๊ธฐ๋ฅผ ํด์ค๋๋ค.
PDFUtil.pdfToBytes()
public byte[] pdfToBytes(PDDocument document) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
document.save(outputStream);
document.close();
return outputStream.toByteArray();
}
Java
๋ณต์ฌ
์ด๋ ๊ฒ ๊ทธ๋ ค์ง PDF ๋ฌธ์๋ฅผ OutputStream์ ๋ด์ ํ byte[]๋ก ๋ณํํด์ค๋๋ค.
์ด์1. ํ์ฌ ์์น๋ฅผ ๊ธฐ์ตํ์ง ๋ชปํ๋ค.
Apache PDFBox์ ContentStream์ ์ด์ด ๋ฌธ์, ์ด๋ฏธ์ง, ๋ํ, ์ ๋ฑ์ ๊ทธ๋ ค๋ฃ๋ ๋ฐฉ์์ผ๋ก PDF๋ฅผ ๊ทธ๋ฆฌ๋๋ฐ
๊ฐ ์์๋ฅผ ๊ทธ๋ฆด ๋๋ง๋ค x, y ์ขํ๋ฅผ ์๋ ค์ฃผ์ด์ผํ๊ณ ContentStream์ close() ํด๋ฒ๋ฆฐ๋ค๋ฉด ์๋ ์ขํ๋ฅผ ์ฌ์ฉํ ์ ์์ด ํ์ฌ ์์น๋ฅผ ์์ด๋ฒ๋ฆฌ๊ฒ ๋ฉ๋๋ค.
public int setFluencies(PDDocument document, Resume resume, int height) throws IOException {
if (resume.getFluencies() == null || resume.getFluencies().isEmpty()) {
return height;
}
PDPage page = document.getPage(document.getPages().getCount() - 1);
int width = (int) page.getMediaBox().getWidth();
resume.getFluencies().sort((c1, c2) -> c2.getAcquisitionDate().compareTo(c1.getAcquisitionDate()));
height = setText(document, fonts.get("extraBold"), 14, blueGray400[0], blueGray400[1], blueGray400[2], margin, height, "์ดํ", 0);
for (int i = 0; i < resume.getFluencies().size(); i++) {
height = setText(document, fonts.get("medium"), 14, black[0], black[1], black[2], margin + padding, height, resume.getFluencies().get(i).getName(), 0);
height = setText(document, fonts.get("regular"), 12, black[0], black[1], black[2], margin + padding + calculatePixel("medium", 14, resume.getFluencies().get(i).getName()), height, resume.getFluencies().get(i).getLanguageType().getName(), 0);
height = setText(document, fonts.get("regular"), 12, black[0], black[1], black[2], width - margin - calculatePixel("regular", 12, resume.getFluencies().get(i).getAcquisitionDate().format(DateTimeFormatter.ofPattern("yyyy.MM"))), height, resume.getFluencies().get(i).getAcquisitionDate().format(DateTimeFormatter.ofPattern("yyyy.MM")), 0);
height -= 26;
height = setText(document, fonts.get("regular"), 12, black[0], black[1], black[2], margin + padding, height, resume.getFluencies().get(i).getGrade(), 0);
if (i != resume.getFluencies().size() - 1) {
height -= 21;
height = setHorizontalLine(document, 1, gray200[0], gray200[1], gray200[2], margin + padding, height, width - margin, height);
}
}
height -= 21;
height = setHorizontalLine(document, 1, gray200[0], gray200[1], gray200[2], margin, height, width - margin, height);
return height;
}
Java
๋ณต์ฌ
์ ์ฝ๋๋ ์ด๋ ฅ์ ์ค ์ดํ์ ๊ดํ ํญ๋ชฉ์ PDF์ ๊ทธ๋ฆฌ๋ ๋ฉ์๋์
๋๋ค.
์ ๋ ์ด ์ด์์ ๋ํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก heigth๋ฅผ ์ง์ ๊ณ์ฐํ๋ ๋ฐฉ์์ ์ ํํ์์ต๋๋ค.
๋ฉ์๋ ์ธ์๋ก ๋ฐ๋ document๋ฅผ ํตํด์๋ PDF ๋ฌธ์ ์ค ๊ฐ์ฅ ๋ง์ง๋ง ํ์ด์ง๋ฅผ ์ ์ ์๊ณ height๋ฅผ ํตํด์๋ ํด๋น ํ์ด์ง์์์ ํ์ฌ ์์น๋ฅผ ์ ์ ์์ต๋๋ค.
setText() ๋ฑ์ ํตํด PDF์ ์์๋ค์ ๊ทธ๋ ค๋ฃ์ผ๋ฉด์๋ heigth๋ฅผ ๊ณ์ฐํด๊ฐ๋ฉด์ ์ต์ข
์ ์ธ ์์น๋ฅผ ๋ฆฌํดํด์ค๋๋ค.
@Slf4j
@Component
public class PDFUtil {
private int margin = 52;
private int padding = 67;
private Map<String, PDType0Font> fonts;
private int[] black = {28, 28, 28};
private int[] blueGray400 = {107, 118, 132};
private int[] gray200 = {226, 228, 230};
private int[] gray300 = {155, 160, 169};
private int[] blue200 = {232, 243, 255};
private int[] blue300 = {41, 97, 205};
private ClassPathResource logo = new ClassPathResource("images/logo.png");
private ClassPathResource uturn = new ClassPathResource("images/uturn.png");
Java
๋ณต์ฌ
๋ํ ์ด๋ฐ์์ผ๋ก margin๊ณผ padding์ ๋ณ์๋ก ์ ์ธํด๋๊ณ ์ฌ์ฉํ์ฌ ์ฝ๋์ ๊ฐ๋
์ฑ์ ๋ํ๊ณ ์ ์ญ์ผ๋ก ์ค์ ์ ๋ณ๊ฒฝํ ์ ์๊ฒ ํด์ค๋๋ค.
ย ์ด์2. ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋๋ฌด ๋ง์ด ์ฌ์ฉํ๋ค.
๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ผ๋ก ์ธํด ์๋น์ค์ ์ํฅ์ด ๊ฐ์ง ์๋๋ก ์ด๋ ฅ์๋ฅผ PDF๋ก ์กฐํํ๋ ๊ธฐ๋ฅ๋ง์ ๋ฐ๋ก AWS Lambda๋ก Generate ์๋ฒ๋ฅผ ๋ถ๋ฆฌํ์์ต๋๋ค.
ย ์ด์3. ๋์ฉ๋ ์ด๋ฏธ์ง ์กฐํ ์ ์๋๊ฐ ๋๋ฌด ๋๋ฆฌ๋ค.
private BufferedImage getImageByResource(ClassPathResource resource) throws IOException {
InputStream inputStream = resource.getInputStream();
BufferedImage image = ImageIO.read(inputStream);
inputStream.close();
return image;
}
Java
๋ณต์ฌ
์ด ๋ถ๋ถ์์ ImageIO.read()๋ก ์ด๋ฏธ์ง์ ์ ์ฒด ๋ฐ์ดํธ๋ฅผ ์ฝ๋ ๊ณผ์ ์์ ์๋๊ฐ ๋๋ ค์ง๋๊ฑธ ๋๋ฒ๊น
์ ํตํด ํ์ธํ์์ต๋๋ค.
if(file.getSize() > 1024 * 1024 * 5) {
throw new CustomException(ErrorCode.EXCEED_SIZE_FILE);
}
try {
try(InputStream inputStream = new ByteArrayInputStream(file.getBytes())) {
BufferedImage buff = ImageIO.read(inputStream);
float width = buff.getWidth();
float height = buff.getHeight();
if (width > 450) {
int resizedWidth = 450;
int resizedHeight = (int) (height / (width / 450));
file = imageUtil.imageResize(file, randomName, extension, resizedWidth, resizedHeight);
}
}
} catch (IOException e) {
throw new CustomException(ErrorCode.IO_ERROR);
}
Java
๋ณต์ฌ
์ด๋ฏธ์ง ์
๋ก๋ ๊ฐ๋ฅ ์ฉ๋์ 5MB๋ก ์ ํํ๊ณ ๊ถ์ฅ ์ฌ์ด์ฆ๋ฅผ 450*600๋ก ์ง์ ํ ํ ํด๋ผ์ด์ธํธ์ธก์๊ฒ 3:4 ํฌ๋กญ์ ๋ถํ๋๋ ค ์ต๋ํ ์ด๋ฏธ์ง๋ฅผ ์ต์ ํํ๊ธฐ๋ก ํ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๊ถ์ฅ ์ฌ์ด์ฆ์ธ width 450์ด ๋์ด๊ฐ๋ฉด ๊ถ์ฅ ์ฌ์ด์ฆ์ ๋ง๊ฒ๋ ๋ฆฌ์ฌ์ด์ง์ ์งํํ๊ธฐ๋ก ํ์ต๋๋ค.
public MultipartFile imageResize(MultipartFile file, String fileName, String fileFormat, int width, int height) {
ByteArrayOutputStream outputStream = null;
try {
int orientation = getOrientation(file);
//MultipartFile to BufferedImage
BufferedImage originalImage = ImageIO.read(file.getInputStream());
//Rotate Image
if (orientation != 1) {
originalImage = rotateImage(originalImage, orientation);
}
//Resize Image
BufferedImage resizedImage = Scalr.resize(originalImage, Scalr.Method.AUTOMATIC, Scalr.Mode.FIT_TO_WIDTH, width, height);
outputStream = new ByteArrayOutputStream();
ImageIO.write(resizedImage, fileFormat, outputStream);
outputStream.flush();
} catch (Exception e) {
throw new CustomException(ErrorCode.IO_ERROR);
}
return new MockMultipartFile(fileName, fileName, file.getContentType(), outputStream.toByteArray());
}
Java
๋ณต์ฌ
๋ฆฌ์ฌ์ด์ง ์ฝ๋๋ ์์ ๊ฐ์ต๋๋ค.