diff --git a/certification/generate_registration_pdf.py b/certification/generate_registration_pdf.py new file mode 100644 index 00000000..9a2bec5e --- /dev/null +++ b/certification/generate_registration_pdf.py @@ -0,0 +1,633 @@ +""" +GUARDiA ITSM 프로그램 등록 신청서 PDF 생성기 +- 소프트웨어 저작권 등록 (한국저작권위원회) +- 소프트웨어사업자 신고 (과학기술정보통신부) +- 조달청 나라장터 물품 등록 +실행: python generate_registration_pdf.py +""" +import os, sys +from pathlib import Path +from datetime import datetime + +from reportlab.lib import colors +from reportlab.lib.pagesizes import A4 +from reportlab.lib.units import mm +from reportlab.lib.styles import ParagraphStyle +from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT, TA_JUSTIFY +from reportlab.platypus import ( + SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, + HRFlowable, PageBreak, KeepTogether +) +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.ttfonts import TTFont + +# ── 폰트 ────────────────────────────────────────────────────── +def reg_fonts(): + for p in ["C:/Windows/Fonts/malgun.ttf", + "/usr/share/fonts/truetype/nanum/NanumGothic.ttf"]: + if os.path.exists(p): + try: + pdfmetrics.registerFont(TTFont("KF", p)); break + except: pass + for p in ["C:/Windows/Fonts/malgunbd.ttf", + "/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf"]: + if os.path.exists(p): + try: + pdfmetrics.registerFont(TTFont("KFB", p)); break + except: pass + +reg_fonts() +BF = "KF" if "KF" in pdfmetrics.getRegisteredFontNames() else "Helvetica" +BBF = "KFB" if "KFB" in pdfmetrics.getRegisteredFontNames() else "Helvetica-Bold" + +W, H = A4 +MARGIN = 18 * mm +WC = W - 2 * MARGIN + +# ── 색상 ────────────────────────────────────────────────────── +NAVY = colors.HexColor("#1e3a5f") +BLUE = colors.HexColor("#0051A2") +ACCENT = colors.HexColor("#00A3E0") +LBLU = colors.HexColor("#EBF3FB") +GRAY = colors.HexColor("#64748B") +LGRAY = colors.HexColor("#F3F4F6") +RED = colors.HexColor("#DC2626") +LRED = colors.HexColor("#FEE2E2") +GREEN = colors.HexColor("#065F46") +LGRN = colors.HexColor("#D1FAE5") +ORANGE = colors.HexColor("#C2410C") +LORG = colors.HexColor("#FED7AA") +WHITE = colors.white +BLACK = colors.black + +# ── 스타일 ──────────────────────────────────────────────────── +def S(name, **kw): + defaults = dict(fontName=BF, fontSize=9.5, leading=15, textColor=BLACK) + defaults.update(kw) + return ParagraphStyle(name, **defaults) + +sty = { + "doc_title": S("dt", fontName=BBF, fontSize=22, textColor=WHITE, alignment=TA_CENTER, leading=28), + "doc_sub": S("ds", fontName=BF, fontSize=11, textColor=colors.HexColor("#BDE3FF"), alignment=TA_CENTER), + "sec": S("sc", fontName=BBF, fontSize=12, textColor=WHITE, leading=18), + "sec2": S("s2", fontName=BBF, fontSize=10.5, textColor=NAVY, leading=16, spaceBefore=6, spaceAfter=3), + "lbl": S("lb", fontName=BBF, fontSize=9.5, textColor=BLACK, alignment=TA_CENTER), + "val": S("vl"), + "val_c": S("vc", alignment=TA_CENTER), + "note": S("nt", fontName=BF, fontSize=8, textColor=GRAY, leading=12, leftIndent=8), + "small": S("sm", fontName=BF, fontSize=8.5, textColor=GRAY, leading=12), + "footer": S("ft", fontName=BF, fontSize=7.5, textColor=GRAY, alignment=TA_CENTER, leading=11), + "req": S("rq", fontName=BBF, fontSize=8, textColor=RED, alignment=TA_CENTER), + "body": S("bd", fontName=BF, fontSize=9.5, leading=15), + "sign": S("sg", fontName=BF, fontSize=10.5, leading=16), + "warn": S("wn", fontName=BBF, fontSize=9, textColor=RED), +} + +# ── 공통 헬퍼 ───────────────────────────────────────────────── +def page_header(title, subtitle, badge_text, badge_color): + """문서 상단 배너.""" + data = [[ + Paragraph(f" {title}", sty["doc_title"]), + Paragraph(subtitle, sty["doc_sub"]), + ]] + t = Table(data, colWidths=[WC]) + t.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,-1), badge_color), + ("TOPPADDING", (0,0), (-1,-1), 14), + ("BOTTOMPADDING", (0,0), (-1,-1), 14), + ("LEFTPADDING", (0,0), (-1,-1), 12), + ("SPAN", (0,0), (0,-1)), + ])) + return t + +def sec_title(text, color=NAVY): + """섹션 제목 배너.""" + t = Table([[Paragraph(f" {text}", sty["sec"])]], colWidths=[WC]) + t.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,-1), color), + ("TOPPADDING", (0,0), (-1,-1), 6), + ("BOTTOMPADDING", (0,0), (-1,-1), 6), + ("ROUNDEDCORNERS",(0,0), (-1,-1), [5]), + ])) + return t + +def info_row(rows, col_w=None, lbg=LBLU): + """키-값 테이블.""" + if col_w is None: + col_w = [40*mm, WC - 40*mm] + cells = [] + for r in rows: + if len(r) == 2: + cells.append([Paragraph(r[0], sty["lbl"]), Paragraph(r[1], sty["val"])]) + else: + cells.append([Paragraph(r[0], sty["lbl"]), + Paragraph(r[1], sty["val"]), + Paragraph(r[2] if len(r)>2 else "", sty["req"])]) + col_w = col_w[:2] + [12*mm] if len(col_w) == 2 else col_w + + t = Table(cells, colWidths=col_w) + t.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (0,-1), lbg), + ("FONTNAME", (0,0), (0,-1), BBF), + ("FONTSIZE", (0,0), (-1,-1), 9.5), + ("ALIGN", (0,0), (0,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 5), + ("BOTTOMPADDING", (0,0), (-1,-1), 5), + ("LEFTPADDING", (0,0), (-1,-1), 7), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + return t + +def sign_box(date_label="신고일"): + """서명 란.""" + rows = [ + [Paragraph(date_label, sty["lbl"]), + Paragraph("2026년 월 일", sty["sign"])], + [Paragraph("회 사 명", sty["lbl"]), + Paragraph("(주)지오정보기술", sty["sign"])], + [Paragraph("대 표 자", sty["lbl"]), + Paragraph(" (인)", sty["sign"])], + ] + t = Table(rows, colWidths=[35*mm, WC-35*mm]) + t.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (0,-1), LBLU), + ("FONTNAME", (0,0), (0,-1), BBF), + ("FONTSIZE", (0,0), (-1,-1), 10), + ("ALIGN", (0,0), (0,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 8), + ("BOTTOMPADDING", (0,0), (-1,-1), 8), + ("LEFTPADDING", (0,0), (-1,-1), 8), + ("LINEBELOW", (1,2), (1,2), 1, BLACK), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + return t + +def footer_bar(page_title): + return [ + Spacer(1, 6*mm), + HRFlowable(width=WC, color=BLUE, thickness=1), + Spacer(1, 2*mm), + Paragraph(f"{page_title} | (주)지오정보기술 | Copyright © 2026 All Rights Reserved.", sty["footer"]), + ] + +# ── BUILD PDF ────────────────────────────────────────────────── +def build(out): + doc = SimpleDocTemplate(out, pagesize=A4, + leftMargin=MARGIN, rightMargin=MARGIN, + topMargin=MARGIN, bottomMargin=MARGIN, + title="GUARDiA ITSM 프로그램 등록 신청서 모음", + author="(주)지오정보기술", + ) + + story = [] + + # ════════════════════════════════════════════════════════════ + # 표지 + # ════════════════════════════════════════════════════════════ + story.append(Spacer(1, 20*mm)) + + # 메인 배너 + cover = Table([[ + Paragraph("프로그램 등록 신청서 모음", S("ct", fontName=BBF, fontSize=28, textColor=WHITE, alignment=TA_CENTER, leading=36)), + Paragraph("GUARDiA ITSM v2.0 | (주)지오정보기술", S("cs", fontName=BF, fontSize=13, textColor=colors.HexColor("#BDE3FF"), alignment=TA_CENTER)), + ]], colWidths=[WC]) + cover.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,-1), NAVY), + ("TOPPADDING", (0,0), (-1,-1), 24), + ("BOTTOMPADDING", (0,0), (-1,-1), 24), + ("ROUNDEDCORNERS",(0,0), (-1,-1), [10]), + ("SPAN", (0,0), (0,-1)), + ])) + story.append(cover) + story.append(Spacer(1, 10*mm)) + + # 포함 문서 목록 + docs_list = [ + ["01", "소프트웨어 저작권 등록 신청서", "한국저작권위원회", BLUE, LBLU], + ["02", "소프트웨어사업자 신고서", "과학기술정보통신부", RED, LRED], + ["03", "조달청 나라장터 물품 등록 신청서", "조달청", ORANGE, LORG], + ] + for no, name, org, fc, bg in docs_list: + row = Table([[ + Paragraph(no, S("n", fontName=BBF, fontSize=16, textColor=WHITE, alignment=TA_CENTER)), + Paragraph(name, S("nm", fontName=BBF, fontSize=12, textColor=fc)), + Paragraph(org, S("og", fontName=BF, fontSize=10, textColor=GRAY)), + ]], colWidths=[14*mm, WC-14*mm-50*mm, 50*mm]) + row.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (0,-1), fc), + ("BACKGROUND", (1,0), (-1,-1), bg), + ("TOPPADDING", (0,0), (-1,-1), 10), + ("BOTTOMPADDING", (0,0), (-1,-1), 10), + ("LEFTPADDING", (0,0), (-1,-1), 10), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("LINEABOVE", (0,0), (-1,0), 0.3, colors.HexColor("#CBD5E1")), + ])) + story.append(row) + story.append(Spacer(1, 2*mm)) + + story.append(Spacer(1, 8*mm)) + + # 제품 요약 + prod = Table([[ + Paragraph("제 품 명", sty["lbl"]), + Paragraph("GUARDiA ITSM (Good AI-based Unified Automated Resource & Device Intelligence Assistant)", sty["val"]), + ],[ + Paragraph("개 발 사", sty["lbl"]), + Paragraph("(주)지오정보기술", sty["val"]), + ],[ + Paragraph("버 전", sty["lbl"]), + Paragraph("2.0.0 (빌드일: 2026-08-31)", sty["val"]), + ],[ + Paragraph("제품 분류", sty["lbl"]), + Paragraph("IT서비스관리(ITSM) / AI 기반 인프라 자동화 플랫폼", sty["val"]), + ]], colWidths=[30*mm, WC-30*mm]) + prod.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (0,-1), LBLU), + ("FONTNAME", (0,0), (0,-1), BBF), + ("FONTSIZE", (0,0), (-1,-1), 9.5), + ("ALIGN", (0,0), (0,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 6), + ("BOTTOMPADDING", (0,0), (-1,-1), 6), + ("LEFTPADDING", (0,0), (-1,-1), 8), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + story.append(prod) + story.append(Spacer(1, 10*mm)) + story.append(Paragraph( + f"※ 본 문서는 GUARDiA ITSM 프로그램 등록을 위한 신청서 모음입니다. 생성일: {datetime.now().strftime('%Y-%m-%d')}", + sty["note"] + )) + story.append(PageBreak()) + + # ════════════════════════════════════════════════════════════ + # 문서 01: 소프트웨어 저작권 등록 신청서 + # ════════════════════════════════════════════════════════════ + story.append(page_header( + "소프트웨어 저작권 등록 신청서", + "컴퓨터프로그램저작물 등록 | 한국저작권위원회", + "신청서 01", BLUE + )) + story.append(Spacer(1, 6*mm)) + story.append(Paragraph("컴퓨터프로그램저작물 등록신청서", S("h1", fontName=BBF, fontSize=14, textColor=NAVY, alignment=TA_CENTER, leading=20))) + story.append(Paragraph("「저작권법」 제53조 및 「저작권법 시행규칙」 제24조에 따라 위와 같이 등록을 신청합니다.", S("ref", fontName=BF, fontSize=8.5, textColor=GRAY, alignment=TA_CENTER, leading=14))) + story.append(Spacer(1, 6*mm)) + + # 저작물 정보 + story.append(sec_title("1. 저작물 정보", BLUE)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["저 작 물 명", "GUARDiA ITSM (가이더)"], + ["영 문 명", "GUARDiA ITSM Platform (Good AI-based Unified Automated Resource & Device Intelligence Assistant)"], + ["저작물 종류", "컴퓨터프로그램저작물"], + ["창 작 연 도", "2026년"], + ["공 표 여 부", "공표"], + ["공 표 연 도", "2026년"], + ["공 표 방 법", "인터넷 웹사이트 (www.zioinfo.co.kr)"], + ["이 용 허 락", "양도 및 이용허락 가능"], + ["버 전", "2.0.0"], + ])) + story.append(Spacer(1, 5*mm)) + + # 저작자 정보 + story.append(sec_title("2. 저작자 정보", BLUE)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["저 작 자 명", "(주)지오정보기술"], + ["법인등록번호", "000000-0000000"], + ["주 소", "서울특별시 (상세주소)"], + ["대 표 자", "(대표이사명)"], + ["전 화", "02-000-0000"], + ["이 메 일", "copyright@zioinfo.co.kr"], + ])) + story.append(Spacer(1, 5*mm)) + + # 저작물 설명 + story.append(sec_title("3. 저작물 설명 (200자 이내)", BLUE)) + story.append(Spacer(1, 3*mm)) + desc = Table([[Paragraph( + "GUARDiA ITSM은 공공기관의 레거시 IT 인프라를 AI로 자율 운영하는 온프레미스 통합 관리 플랫폼입니다. " + "메신저 한 줄 명령으로 에이전트 설치 없이 SSH/SFTP를 통해 WAS 배포·운영을 자동화하며, " + "SR 관리, 인시던트 대응, 변경관리, CMDB, PMS 등 ITSM 전 기능을 제공합니다. " + "Python 3.11/FastAPI 백엔드, React 18 프론트엔드, Ollama 온프레미스 AI 엔진 사용.", + sty["body"] + )]], colWidths=[WC]) + desc.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,-1), LBLU), + ("TOPPADDING", (0,0), (-1,-1), 10), + ("BOTTOMPADDING", (0,0), (-1,-1), 10), + ("LEFTPADDING", (0,0), (-1,-1), 12), + ("RIGHTPADDING", (0,0), (-1,-1), 12), + ("ROUNDEDCORNERS",(0,0), (-1,-1), [5]), + ])) + story.append(desc) + story.append(Spacer(1, 5*mm)) + + # 프로그램 언어 + story.append(sec_title("4. 프로그램 작성 언어 및 환경", BLUE)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["프로그램 언어", "Python 3.11, JavaScript (ECMAScript 2023)"], + ["프레임워크", "FastAPI 0.115, React 18.3, SQLAlchemy 2.0"], + ["운영 환경", "Linux (Ubuntu/CentOS/RHEL), Windows Server 2019+"], + ["소스 규모", "Python 145파일 52,833줄, JavaScript 50파일 약 8,000줄"], + ])) + story.append(Spacer(1, 5*mm)) + + # 제출 서류 + story.append(sec_title("5. 첨부 서류", BLUE)) + story.append(Spacer(1, 3*mm)) + attach = [ + ["1", "저작물 설명서", "본 문서 3항 활용"], + ["2", "소스코드 일부", "핵심 모듈 200줄 이상 (영업비밀 마스킹 후)"], + ["3", "법인등기부등본", "최근 3개월 이내"], + ["4", "대리인 위임장", "대리신청 시에만 해당"], + ] + ah = [["번호", "서류명", "비고"]] + at = Table( + [[Paragraph(v, S(f"a{i}", alignment=TA_CENTER if i==0 else TA_LEFT, fontSize=9)) for i,v in enumerate(r)] for r in ah+attach], + colWidths=[12*mm, 70*mm, WC-82*mm] + ) + at.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,0), BLUE), + ("TEXTCOLOR", (0,0), (-1,0), WHITE), + ("FONTNAME", (0,0), (-1,0), BBF), + ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LBLU]), + ("FONTSIZE", (0,0), (-1,-1), 9), + ("ALIGN", (0,0), (0,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 5), + ("BOTTOMPADDING", (0,0), (-1,-1), 5), + ("LEFTPADDING", (0,0), (-1,-1), 7), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + story.append(at) + story.append(Spacer(1, 6*mm)) + + # 신청 기관 안내 + story.append(Paragraph("【 제출처 】 한국저작권위원회 | www.copyright.or.kr | Tel: 1800-5455 | 수수료: 약 40,000원", sty["note"])) + story.append(Spacer(1, 6*mm)) + story.append(sign_box("신 청 일")) + story.append(Spacer(1, 3*mm)) + story.append(Paragraph("위와 같이 저작물 등록을 신청합니다.", S("sc2", fontName=BF, fontSize=9.5, alignment=TA_CENTER))) + + for item in footer_bar("소프트웨어 저작권 등록 신청서"): + story.append(item) + story.append(PageBreak()) + + # ════════════════════════════════════════════════════════════ + # 문서 02: 소프트웨어사업자 신고서 + # ════════════════════════════════════════════════════════════ + story.append(page_header( + "소프트웨어사업자 신고서", + "소프트웨어진흥법 제24조 | 과학기술정보통신부", + "신청서 02", RED + )) + story.append(Spacer(1, 6*mm)) + story.append(Paragraph("소프트웨어사업자 신고서", S("h2", fontName=BBF, fontSize=14, textColor=RED, alignment=TA_CENTER, leading=20))) + story.append(Paragraph("「소프트웨어진흥법」 제24조 및 같은 법 시행규칙 제14조에 따라 신고합니다.", S("ref2", fontName=BF, fontSize=8.5, textColor=GRAY, alignment=TA_CENTER, leading=14))) + story.append(Spacer(1, 6*mm)) + + story.append(sec_title("1. 신고인 (사업자) 정보", RED)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["상 호", "(주)지오정보기술"], + ["대 표 자", "(대표이사명)"], + ["사업자등록번호", "000-00-00000"], + ["법인등록번호", "000000-0000000"], + ["소 재 지", "서울특별시 (상세주소)"], + ["전 화", "02-000-0000"], + ["팩 스", "02-000-0001"], + ["이 메 일", "sw@zioinfo.co.kr"], + ["설 립 일", "2000년 월 일"], + ["자 본 금", " 원"], + ], lbg=LRED)) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("2. 소프트웨어사업의 종류", RED)) + story.append(Spacer(1, 3*mm)) + biz_types = [ + ("V", "소프트웨어 개발업", "GUARDiA ITSM 등 소프트웨어 개발"), + ("V", "소프트웨어 공급업", "GUARDiA ITSM 라이선스 공급"), + ("V", "소프트웨어 유지관리업", "GUARDiA ITSM 유지보수 및 기술지원"), + ("V", "소프트웨어 자문·평가·진단업", "IT인프라 컨설팅"), + (" ", "정보기술서비스업", "해당 없음"), + ] + bh = [["선택", "사업 종류", "세부 내용"]] + bd = [[Paragraph(v if v==" " else "[V]", S(f"bt{i}", alignment=TA_CENTER, textColor=RED if v=="V" else GRAY, fontSize=10)), + Paragraph(n, S(f"bn{i}", fontSize=9.5)), + Paragraph(d, S(f"bd{i}", fontSize=8.5, textColor=GRAY))] + for i,(v,n,d) in enumerate(biz_types)] + bt = Table(bh+bd, colWidths=[16*mm, 65*mm, WC-81*mm]) + bt.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,0), RED), + ("TEXTCOLOR", (0,0), (-1,0), WHITE), + ("FONTNAME", (0,0), (-1,0), BBF), + ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LRED]), + ("FONTSIZE", (0,0), (-1,-1), 9), + ("ALIGN", (0,0), (0,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 5), + ("BOTTOMPADDING", (0,0), (-1,-1), 5), + ("LEFTPADDING", (0,0), (-1,-1), 7), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + story.append(bt) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("3. 기술인력 현황", RED)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["총 직원 수", " 명"], + ["SW 기술인력", " 명 (전체의 %)"], + ["정보처리기사", " 명"], + ["정보처리산업기사"," 명"], + ["기타 SW 자격", " 명"], + ], lbg=LRED)) + story.append(Spacer(1, 3*mm)) + story.append(Paragraph("※ 소프트웨어사업자 신고 기준: 상시근로자 1명 이상 또는 SW기술인력 1명 이상", sty["note"])) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("4. SW사업 실적 (최근 3년)", RED)) + story.append(Spacer(1, 3*mm)) + perf_h = [["연도", "발주기관", "사업명", "계약금액(백만원)", "기간"]] + perf_d = [ + ["2024", "(공공기관명)", "GUARDiA ITSM 구축사업", "", ""], + ["2025", "(공공기관명)", "인프라 자동화 컨설팅", "", ""], + ["2026", "(공공기관명)", "GUARDiA ITSM 고도화", "", ""], + ] + pt = Table(perf_h+perf_d, colWidths=[14*mm, 40*mm, 60*mm, 30*mm, WC-144*mm]) + pt.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,0), RED), + ("TEXTCOLOR", (0,0), (-1,0), WHITE), + ("FONTNAME", (0,0), (-1,0), BBF), + ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LRED]), + ("FONTSIZE", (0,0), (-1,-1), 8.5), + ("ALIGN", (0,0), (-1,0), "CENTER"), + ("ALIGN", (0,1), (0,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 5), + ("BOTTOMPADDING", (0,0), (-1,-1), 5), + ("LEFTPADDING", (0,0), (-1,-1), 5), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + story.append(pt) + story.append(Spacer(1, 5*mm)) + + story.append(Paragraph("【 신고처 】 한국SW산업협회(KOSA) / 과학기술정보통신부 | swit.or.kr | 수수료: 없음", sty["note"])) + story.append(Spacer(1, 5*mm)) + story.append(sign_box("신 고 일")) + + for item in footer_bar("소프트웨어사업자 신고서"): + story.append(item) + story.append(PageBreak()) + + # ════════════════════════════════════════════════════════════ + # 문서 03: 조달청 나라장터 물품 등록 신청서 + # ════════════════════════════════════════════════════════════ + story.append(page_header( + "조달청 나라장터 물품 등록 신청서", + "국가종합전자조달시스템 | 조달청", + "신청서 03", ORANGE + )) + story.append(Spacer(1, 6*mm)) + story.append(Paragraph("소프트웨어 물품 등록 신청서", S("h3", fontName=BBF, fontSize=14, textColor=ORANGE, alignment=TA_CENTER, leading=20))) + story.append(Paragraph("「국가를 당사자로 하는 계약에 관한 법률」에 따라 공공기관 납품을 위한 물품을 등록합니다.", S("ref3", fontName=BF, fontSize=8.5, textColor=GRAY, alignment=TA_CENTER, leading=14))) + story.append(Spacer(1, 6*mm)) + + story.append(sec_title("1. 업체 정보", ORANGE)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["업 체 명", "(주)지오정보기술"], + ["대 표 자", "(대표이사명)"], + ["사업자등록번호", "000-00-00000"], + ["소 재 지", "서울특별시 (상세주소)"], + ["대표 전화", "02-000-0000"], + ["담당자명", "(담당자명) | 010-0000-0000"], + ["이 메 일", "g2b@zioinfo.co.kr"], + ], lbg=LORG)) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("2. 물품 기본 정보", ORANGE)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["물 품 명", "GUARDiA ITSM (AI 기반 IT서비스관리 플랫폼)"], + ["규격/모델명", "GUARDiA ITSM v2.0"], + ["물품 분류코드", "소프트웨어 > 시스템관리 > IT서비스관리"], + ["단 위", "식 (라이선스)"], + ["원산지", "국내산 (대한민국)"], + ["제조사명", "(주)지오정보기술"], + ["브랜드명", "GUARDiA"], + ], lbg=LORG)) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("3. 가격 정보 (라이선스 에디션별)", ORANGE)) + story.append(Spacer(1, 3*mm)) + price_h = [["에디션", "대상", "기준 단가(원)", "유지보수율", "비고"]] + price_d = [ + ["COMMUNITY", "소규모/검토용", "0", "—", "무료"], + ["STANDARD", "중형 기관", "별도 협의", "15%", "서버 50대·사용자 200명"], + ["ENTERPRISE", "대형 관공서", "별도 협의", "15%", "무제한"], + ] + prices = Table(price_h+price_d, colWidths=[28*mm, 30*mm, 38*mm, 22*mm, WC-118*mm]) + prices.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,0), ORANGE), + ("TEXTCOLOR", (0,0), (-1,0), WHITE), + ("FONTNAME", (0,0), (-1,0), BBF), + ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LORG]), + ("FONTSIZE", (0,0), (-1,-1), 9), + ("ALIGN", (0,0), (-1,0), "CENTER"), + ("ALIGN", (0,1), (-1,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 5), + ("BOTTOMPADDING", (0,0), (-1,-1), 5), + ("LEFTPADDING", (0,0), (-1,-1), 5), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + story.append(prices) + story.append(Spacer(1, 3*mm)) + story.append(Paragraph("※ 나라장터 등록 단가는 실제 계약 시 협의를 통해 결정됩니다.", sty["note"])) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("4. 제품 규격 (설치 환경)", ORANGE)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["서버 OS", "Ubuntu 20.04+ / CentOS 7+ / RHEL 8+ / Windows Server 2019+"], + ["최소 CPU", "4코어 이상 (Intel/AMD x86_64)"], + ["최소 RAM", "16GB 이상 (Ollama AI 엔진 포함)"], + ["디스크 용량", "50GB 이상"], + ["네트워크", "내부망 전용 가능 (인터넷 연결 불필요 — 완전 폐쇄망 지원)"], + ["클라이언트", "Chrome 90+ / Firefox 88+ / Edge 90+ (브라우저 기반)"], + ["DB", "SQLite (내장) / PostgreSQL 15+ (별도 설치)"], + ], lbg=LORG)) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("5. 주요 기능 요약", ORANGE)) + story.append(Spacer(1, 3*mm)) + funcs = [ + ("SR/ITSM", "서비스요청 접수·처리, 인시던트, 변경관리, SLA, CMDB"), + ("AI 자동화", "Ollama 온프레미스 AI — 티켓분류·RCA·이상탐지·예측"), + ("ChatOps", "카카오워크·네이버웍스·슬랙 25개 봇 명령어"), + ("에이전트리스", "SSH/SFTP 기반 WAS 자동 배포·운영 (설치 없음)"), + ("PMS", "WBS·산출물·일간/주간/월간 보고서 자동 생성"), + ("보안", "JWT+MFA+AES-256+PAM+감사로그+취약점스캔"), + ] + fh = [["기능 영역", "내용"]] + fd = [[Paragraph(k, S(f"fk{i}", fontName=BBF, fontSize=9)), Paragraph(v, S(f"fv{i}", fontSize=9))] for i,(k,v) in enumerate(funcs)] + ft = Table(fh+fd, colWidths=[30*mm, WC-30*mm]) + ft.setStyle(TableStyle([ + ("BACKGROUND", (0,0), (-1,0), ORANGE), + ("TEXTCOLOR", (0,0), (-1,0), WHITE), + ("FONTNAME", (0,0), (-1,0), BBF), + ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LORG]), + ("FONTSIZE", (0,0), (-1,-1), 9), + ("ALIGN", (0,0), (0,-1), "CENTER"), + ("VALIGN", (0,0), (-1,-1), "MIDDLE"), + ("TOPPADDING", (0,0), (-1,-1), 5), + ("BOTTOMPADDING", (0,0), (-1,-1), 5), + ("LEFTPADDING", (0,0), (-1,-1), 7), + ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#CBD5E1")), + ])) + story.append(ft) + story.append(Spacer(1, 5*mm)) + + story.append(sec_title("6. 납품 실적 및 인증 현황", ORANGE)) + story.append(Spacer(1, 3*mm)) + story.append(info_row([ + ["GS인증", "GS 1등급 취득 예정 (TTA, 2026년 12월)"], + ["SW저작권 등록", "C-2026-XXXXXX (등록 후 기입)"], + ["SW사업자 신고", "제XXX호 (신고 후 기입)"], + ["주요 납품 실적", "(공공기관 납품 실적 기입)"], + ], lbg=LORG)) + story.append(Spacer(1, 5*mm)) + + story.append(Paragraph("【 등록처 】 조달청 나라장터 (g2b.go.kr) → 업체 로그인 → 물품 등록 신청 | 수수료: 없음", sty["note"])) + story.append(Spacer(1, 5*mm)) + story.append(sign_box("신 청 일")) + + for item in footer_bar("조달청 나라장터 물품 등록 신청서"): + story.append(item) + + # ── 빌드 ────────────────────────────────────────────────── + def on_page(canvas, doc): + canvas.saveState() + canvas.setFont("Helvetica", 7) + canvas.setFillColor(GRAY) + canvas.drawRightString(W-MARGIN, 9*mm, f"- {doc.page} -") + canvas.drawString(MARGIN, 9*mm, "GUARDiA ITSM 프로그램 등록 신청서 | (주)지오정보기술") + canvas.restoreState() + + doc.build(story, onFirstPage=on_page, onLaterPages=on_page) + size = Path(out).stat().st_size / 1024 + print(f"\n[OK] PDF 생성 완료: {out}") + print(f" 크기: {size:.1f} KB / 4페이지 (표지+3종 신청서)") + + +if __name__ == "__main__": + import sys + sys.stdout.reconfigure(encoding='utf-8', errors='replace') + out = Path(__file__).parent / "프로그램등록_신청서_GUARDiA_ITSM_v2.0.pdf" + print("GUARDiA ITSM 프로그램 등록 신청서 PDF 생성 중...") + build(str(out)) diff --git a/certification/프로그램등록_신청서_GUARDiA_ITSM_v2.0.pdf b/certification/프로그램등록_신청서_GUARDiA_ITSM_v2.0.pdf new file mode 100644 index 00000000..499df1cb Binary files /dev/null and b/certification/프로그램등록_신청서_GUARDiA_ITSM_v2.0.pdf differ