본문 바로가기
Java

[java] pdf 생성

by Yeoseungwon 2024. 6. 19.
728x90
public String signInsert(@ModelAttribute("searchVO") UserVO vo,Model model, HttpServletRequest request) throws Exception {
		logger.info("CALL /signInsert.do");

		//pdf생성
		String base64Code = "";
		// 현재 시간
		String currentTime = DateUtil.getCurrentDateTime();
		String signKeyNm="";
		String pdfNm = "_sign_".concat(currentTime);
		//이미지없이 데이터만 넣은 pdf파일
		String SRC = "";
		//firstPdf + 이미지 합쳐진 최종 pdf파일
		String DESC = "";
		UserMembershipVO resultList = new UserMembershipVO();

		String message = "";
		String returnUrl = "/sign.do";
		Document document = new Document(PageSize.A4, 50, 50, 50, 50); // 용지 및 여백 설정
		String storePathString = "";

		try {

			//계약서에 입력될 회원정보 select
			UserMembershipVO mVO = new UserMembershipVO();
			mVO.setSignKey(vo.getSignKey());
			mVO.setSign(vo.getSign());
			mVO.setMembershipNo(vo.getMembershipNo());
			resultList = signService.selectPdfSignInfo(mVO); //membershipNo , signKey

			if(null != resultList && "S3".equals(resultList.getStep())) {
				message = "이미 서명한 계약서입니다.";
				model.addAttribute("message", message);
				return "reservation/sign/result";
			}

			vo.setStep("S3"); // 서명완료

			// 사인 이미지 base64 insert (USER_INFO_MEMBERSHIP_SIGN)
			int resultCnt = signService.signInsert(vo);

			logger.info("resultCnt={}",resultCnt);
			if (resultCnt == 1) {

				message = "서명 완료되었습니다.";

				PdfWriter writer = null;
				XMLWorker worker = null;
				XMLParser xmlParser = null;
				StringReader strReader = null;
				try {

					// 사인 완료된 계약서 pdf로 저장
					String signType = resultList.getSignType();
					logger.info("signType={}", signType);

					vo.setOrderId(resultList.getOrderId());
					UserVO orderMasterResult = signService.orderMasterResult(vo); //orderId

					// 회원권정보
					List<UserVO> orderDetailList = signService.orderDetailList(vo);
					String memberTicket = "";
					for (UserVO orderDetail : orderDetailList) {
						memberTicket += orderDetail.getCategoryNm() + " ";
						memberTicket += orderDetail.getTicketNm() + " ";
						memberTicket += orderDetail.getStartDate() + " ~ ";
						memberTicket += orderDetail.getEndDate() + "";
					}
					logger.info("memberTicket={}", memberTicket);

					// groupId 로 Code조회
					GroupVO gvo = new GroupVO();
					gvo.setGroupId(vo.getGroupId());
					GroupVO groupInfo = groupService.selectInfo(gvo);
					String groupNm = groupInfo.getGroupNm();
					logger.info("groupNm={}",groupNm);

					// 회원권정보
					Map reservationMap = signService.selectOrderInfo(vo);
					List<Map> itemList = (List) reservationMap.get("select_item_info_list");


					//파일저장경로 fileStorePath = file.upload-dir =/home/reservation/uploads/

					signKeyNm = vo.getSignKey();
					logger.info("signKeyNm={}", signKeyNm);
					// pdf 파일 이름
					// 파일이 저장될 폴더경로
					storePathString = groupInfo.getGroupCode() + "/" +signType;
					logger.info("storePathString={}",storePathString);

					SRC = fileStorePath + storePathString+"/"+pdfNm+".pdf";
					logger.info("SRC = {}",SRC);
					File saveFolder = new File(WebUtil.filePathBlackList(fileStorePath + storePathString));

					if (!saveFolder.exists() || saveFolder.isFile()) {
						logger.info("saveFolder create");
						saveFolder.mkdirs();
					}

					File signFile = new File(WebUtil.filePathBlackList(SRC));
					if (signFile.exists()) {
						logger.info("################## SLEEP #################");
						Thread.sleep(3);
					}


					String gender = "";
					if(resultList.getMemberGender().equals("W")) {
						gender = "여";
					}else if(resultList.getMemberGender().equals("M")) {
						gender = "남";
					}
					logger.info("MemberGender={}", resultList.getMemberGender());
					logger.info("gender={}", gender);
					base64Code = vo.getSign();
					logger.info("base64Code={}", base64Code);

					writer = PdfWriter.getInstance(document, new FileOutputStream(SRC)); //현재상대경로에 first.pdf 생성
					writer.setInitialLeading(12.5f);

					document.open(); //생성된 파일을 오픈
					logger.info("document.open");
					logger.info("document.isOpen()={}", document.isOpen());
					XMLWorkerHelper helper = XMLWorkerHelper.getInstance();

					// 사용할 CSS 를 준비한다.
					CSSResolver cssResolver = new StyleAttrCSSResolver();

					String cssPath = fileStorePath+"pdf.css";
					File cssFilePath = new File(WebUtil.filePathBlackList(cssPath));
					if(!cssFilePath.exists()) {
						logger.info("NOT FOUND cssPath={}", cssPath);
					}
					CssFile cssFile = helper.getCSS(new FileInputStream(cssPath));

					cssResolver.addCss(cssFile);

					// HTML 과 폰트준비
					XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
					fontProvider.register(fileStorePath+"malgun.ttf","MalgunGothic"); // MalgunGothic 은 alias,
					CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);

					HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
					htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());

					// Pipelines
					PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
					HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
					CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);

					worker = new XMLWorker(css, true);
					xmlParser = new XMLParser(worker, Charset.forName("UTF-8"));

					// 폰트 설정에서 별칭으로 줬던 "MalgunGothic"을 html 안에 폰트로 지정한다.
					String htmlStr = "<html><head><body style='font-family: MalgunGothic;'>"
							+ "<div id='popupDiv'>"
							+ 	"<div class='popup' id='popup' style='display:block'>"
							+		"<div class='wrapper popupwrap'>"
							+			"<div class='contents' id='contents'>"
							+				"<div class='sign-form'>"
							+					"<div class='title'>"
							+						"<h1>CLUB breex 회원가입 신청완료</h1>"
							+						"<div class='form'>"
							+							"<section>"
							+								"<h2 class='info-h2'>회원 정보</h2>"
							+								"<div class='table'>"
							+									"<table class='sign-table'>"
							+										"<tbody>"
							+											"<tr>"
							+												"<th>회원성명</th>"
							+												"<td>"+resultList.getMemberName()+"</td>"
							+												"<th>연락처</th>"
							+												"<td>"+resultList.getMemberTel()+"</td>"
							+											"</tr>"
							+											"<tr>"
							+												"<th>성별</th>"
							+												"<td>"+gender+"</td>"
							+												"<th>생년월일</th>"
							+												"<td>"+resultList.getMemberBirthday()+"</td>"
							+											"</tr></tbody></table></div></section>"
							+"<section>"
							+	"<h2 class='ticket-info-h2'>회원권 정보</h2>"
							+		"<div class='table'>"
							+			"<table class='sign-table'>"
							+				"<tbody>"
							+					"<tr>"
							+						"<th style='width:5.5%;'>결제일</th>"
							+						"<td colspan='3'>"+ orderMasterResult.getPaymentDate()  +"</td>"
							+					"</tr>"
							+					"<tr>"
							+						"<th style='width:5.5%;'>회원권</th>"
							+						"<td colspan='3'>";

					for(Map item : itemList) {
						Map map = (Map) item.get("item_ticket_info");
						String pageNm = SF_StringUtil.isNullToString(item.get("page_nm"));
						String categoryNm = SF_StringUtil.isNullToString(item.get("category_nm"));
						String itemNm = SF_StringUtil.isNullToString(item.get("item_nm"));
						String ticketNm = SF_StringUtil.isNullToString(map.get("ticket_nm"));
						String startDate = "";
						String endDate = "";
						String lessonYn = SF_StringUtil.isNullToString(map.get("lesson_yn"));
						if(lessonYn.equals("Y")) {
							Map lessonMap = (Map) item.get("lesson_info");
							startDate = SF_StringUtil.isNullToString(lessonMap.get("lesson_start_date"));
							endDate = SF_StringUtil.isNullToString(lessonMap.get("lesson_end_date"));
						} else {
							startDate = SF_StringUtil.isNullToString(item.get("start_date"));
							endDate = SF_StringUtil.isNullToString(item.get("end_date"));
						}

						htmlStr	+=	"<p>"
								+ 	pageNm;

						if(!pageNm.equals(categoryNm)) {
							htmlStr	+= " "+categoryNm;
						}
						htmlStr	+=	" "+itemNm +" "+ ticketNm +" "
								+ 	startDate+"~"+endDate
								+	"</p>";
					}

					DecimalFormat formatter = new DecimalFormat("###,###,###,###");
					String totalPrice = formatter.format(SF_Utils.convertInteger(resultList.getTotalPrice()));

					htmlStr+=""
							+						"</td>"
							+       			"</tr>"
							+       			"<tr>"
							+						"<th style='width:5.5%;'>메모</th>"
							+						"<td colspan='3'>"+reservationMap.get("memo")
							+						"</td>"
							+       			"</tr>"
							+				"</tbody>"
							+ 			"</table>"
							+			"<div class='total-price'>"
							+				"<span class='total-title'>총 결제 금액</span>"
							+				"<span class='space'>...</span>"
							+				"<span class='price'>"+totalPrice+"</span>"
							+			"</div></div>"
							+"</section>"
							+"<section>"
							+	"<h2>이용 약관</h2>"
							+	"<div class='agreement'>"
							+		"<h2>제 1조(목적)</h2>"
							+		"<p>본 회칙은 "+groupNm+"의 효율적인 관리를 목적으로 한다.</p>"
							+		"<h2>제 2조(운영시간)</h2>"
							+		"<p>주중 오전 6시부터 오후 11시까지 / 토요일 오전 9시부터 오후 6시까지 / 일요일 및 법정 공휴일은 휴무일로 정함.</p>"
							+ 		"<p>대체공휴일 등 운영에 대한 변동이 있을 경우, 회원관리 앱 또는 게시판을 통해 별도 공지.</p>"
							+ 		"<h2>제 3조(이용 요금 및 입장 제한)</h2>"
							+ 			"<p>1. 일일입장권 : 44,000원/ 1개월: 200.000원 / 3개월 : 480.000원 / 6개월 : 900.000원/ 12개월 : 1.680.000원 (모든 상품 VAT포함)</p>"
							+ 			"<p>2. 1일 1회 입장. 3시간 이용 원칙 (2024년 1월 1일부터)</p>"
							+		"<h2>제 4조(환불)</h2>"
							+ 			"<p class='red underline'>1. 회원권은 위약금 10% 차감 후 사용 일수 X 1일 6,666원씩을 공제 후 환불 (14일 이내 환불금 처리 / 일 회권은 제외)</p>"
							+ 			"<p class='red underline'>2. 양도받은 회원권은 환불과 휴회 불가.</p>"
							+			"<p class='red underline'>3. 개인 사물함 미사용 일수는 1개월 단위로 환불 가능</p>"
							+ 			"<p class='red underline'>4. 결제일로부터 10일 이후 또는 회원권 개시 이후에는 위약금 10% 발생.</p>"
							+			"<p>5. 가입 최초 1회 제공되는 OT 수업은 회원권을 유지하는 회원에 한해서 제공되는 특별 제공 세션으로, 회원권을 한 달 이상 유지하지 않고 환불 시 정상가로(44.000원) 차감 후 환불된다.</p>"
							+		"<h2>제 5조(명의 변경 및 휴회 신청)</h2>"
							+			"<p class='red underline'>1. 휴회 신청은 회원권 등록자에 한해 회원권 상품에 따라 최소 14일~70일까지 사전 신청이 가능하다. (당일 휴회 신청 불가)</p>"
							+			"<p class='red underline'>2. 명의변경 및 양도는 상품을 등록한 회원에 한하며 1회만 가능 (양도비용 100.000원. 재양도 및 중지 불가. 보유 포인트 양도 불가)</p>"
							+			"<p class='red underline'>3. 1개월권 : 해당 없음 / 3개월권 : 14일 / 6개월권 : 30일 / 12개월권 : 60일</p>"
							+			"<p>*기본 제공 휴회 일을 다 소진한 후, 불가피한 상황 발생 시 증빙자료 제출을 통해 추가 휴회 가능(단, 총 휴회 일이 기본 제공 휴회 일의 2배를 넘길 수 없다)</p>"
							+		"<h2>제 6조(손해배상 책임)</h2>"
							+ 			"<p>1.귀중품은 CCTV 확인 가능한 개인사물함(락커룸 제외) 또는 카운터에 보관한다. CCTV 확인 신청은 15일 이내로 가능하며 센터 책임자를 통해 확인 및 피드백을 받는다.</p>"
							+ 			"<p>2. 본인 부주의로 인한 개인 물품 손상 및 도난은 본인에게 책임이 있으며 본 센터의 책임은 없다.</p>"
							+ 			"<p>3. 센터 내의 서비스 시설 및 운동 장비의 결함으로 인하여 입은 피해는 센터에서 손해배상해 준다.</p>"
							+ 			"<p>4. 개인 사물함 갱신 기간이 일주일 경시 물품을 회수하여 보관하며, 보관 1개월 경과 후에는 자체 폐기처리 한다.<br />(앱에서 기간확인 가능하며 폐기직전 본 센터에서는 개별 연락을 취하지 않는다.)</p>"
							+		"<h2>제 7조(회원 의무 준수)</h2>"
							+ 			"<p>1. 센터에서 정하는 회원 출입관리 절차를 준수한다 (회원번호 체크인 후 센터 및 락커룸 이용)</p>"
							+ 			"<p>2. 회원권 만료 후 시설 무단 이용. 비회원 동반 입장. 회원간 다툼. 건전한 운동 문화를 저해하는 행위 등 센터의 규율에 어긋나는 행위 적발시 강제 달회 조치 및 법적 책임을 물을 수 있다.(재가입 및 환불 불가)</p>"
							+ 		"<h2>제 8조(효력 발생)</h2>"
							+ 		"<p>본 회칙의 효력은 입장 시작일로부터 발생된다.</p>"
							+	"</div>"
							+"</section>"
							+"<div class='sign-area' id='sign2'>"
							+	"<div class='info'>"
							+		"<h3>"+groupNm+" 운영회칙 준수 동의서</h3>"
							+		"<p>상기 본인은 "+groupNm+" 회원으로 가입함에 있어 회칙, 세칙, 규정 등 준수사항을 충분히 숙지하고 이해하였습니다.</p>"
							+	"</div>"
							+	"<div class='signature'>"
							+		"<span class='year'>"+vo.getSignYear()+"년</span> <span class='space'>.</span>"
							+		"<span class='month'>"+vo.getSignMonth()+"월</span> <span class='space'>.</span>"
							+		"<span class='day'>"+vo.getSignDay()+"일</span> <span class='space'>.</span>"
							+		"<span class='name'>회원명<span class='space'>.</span>"+resultList.getSignName()+"</span>"
							+	"</div>"
							+"</div>"
							+ "</div></div></div></div></div></div></div>"
							+ "</body></head></html>";

					logger.info("htmlStr={}", htmlStr);
					strReader = new StringReader(htmlStr);
					xmlParser.parse(strReader);
					//		        	if(writer != null) {
					//		        		writer.flush();
					//		        	}
					//		        	if(xmlParser != null) {
					//		        		xmlParser.flush();
					//		        	}
					logger.info("document.isOpen()={}", document.isOpen());
					if(document.isOpen()) {
						logger.info("document.close()");
						document.close();
					}
					//		            document.close();
					//		            writer.close();
				} catch (DocumentException e) {
					logger.error(e.getMessage(), e);
				} catch (FileNotFoundException e) {
					logger.error(e.getMessage(), e);
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				} finally {
					logger.info("finally");
					if(strReader != null) {
						logger.info("strReader.close()");
						strReader.close();
					}
					if(worker != null) {
						logger.info("worker.close()");
						worker.close();
					}
					if(writer != null) {
						logger.info("writer.close()");
						writer.close();
					}
					logger.info("document.isOpen()={}", document.isOpen());
					if(document != null && document.isOpen()) {
						logger.info("document.close()");
						document.close();
					}
				}

				//PFD 파일에 이미지를 넣어본다.
				PdfReader reader = null;
				PdfStamper stamper = null;
				try {

					//첫번째 만들어진 PDF
					logger.info("SRC={}", SRC);
					File srcFolder = new File(WebUtil.filePathBlackList(SRC));
					logger.info("srcFolder.exists()={}", srcFolder.exists());
					logger.info("srcFolder.isFile()={}", srcFolder.isFile());

					reader = new PdfReader(SRC);

					//이미지랑 합쳐질 PDF
					DESC = fileStorePath + storePathString+ "/" +signKeyNm + ".pdf";
					logger.info("DESC={}", DESC);
					File descFolder = new File(WebUtil.filePathBlackList(DESC));
					logger.info("descFolder.exists()={}", descFolder.exists());
					logger.info("descFolder.isFile()={}", descFolder.isFile());

					stamper = new PdfStamper(reader, new FileOutputStream(descFolder));

					logger.info("이미지를 읽습니다");
					PdfContentByte over = stamper.getOverContent(1);

					// base64 이미지변환
					logger.info("base64Code(B)={}", base64Code);
					base64Code = base64Code.replace("data:image/png;base64,", "");
					logger.info("base64Code(A)={}", base64Code);
					Image image = Image.getInstance(Base64.getDecoder().decode(base64Code));
					float newWidth = 100;
					float newHeight = 60;
					image.scaleAbsolute(newWidth, newHeight);
					image.setAbsolutePosition(300, 180);
					over.addImage(image);

					//		            stamper.close();
					//		            reader.close();

					model.addAttribute("resultList", resultList);
					message = "서명이 완료되었습니다.";
					//		            model.addAttribute("message", message);
					//					model.addAttribute("returnUrl", returnUrl);
				} catch (BadElementException e) {
					logger.error(e.getMessage(), e);
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				} catch (BadPdfFormatException e) {
					logger.error(e.getMessage(), e);
				} catch (DocumentException e) {
					logger.error(e.getMessage(), e);
				} finally {
					logger.info("finally");
					if(stamper != null) {
						stamper.close();
					}
					if(reader != null) {
						reader.close();
					}
				}

			} else {
				message = "서명 중 오류가 발생하였습니다1.";
			}

		} catch (Exception | Error e) {

			logger.error(e.getMessage(), e);
			message = "서명 중 오류가 발생하였습니다2.";
		} finally {
			logger.info("message={}", message);
			logger.info("returnUrl={}", returnUrl);
			model.addAttribute("message", message);
			model.addAttribute("returnUrl", returnUrl);
		}

		return "reservation/sign/result";

	}
728x90