zioinfo-mail/certification/generate_each_pdf.py
DESKTOP-TKLFCPR\ython 6ad7a158c8 feat(cert): 프로그램 등록 신청서 3종 DOCX + 저작권 등록용 소스코드
[DOCX 3종 생성 (UTF-8, 편집 가능)]
- 01_소프트웨어_저작권_등록_신청서.docx  (37KB)
  한국저작권위원회 제출용 / 맑은 고딕 / 색상 섹션
- 02_소프트웨어사업자_신고서.docx         (37KB)
  과학기술정보통신부/KOSA 제출용
- 03_조달청_나라장터_물품_등록_신청서.docx (38KB)
  공공기관 납품용 나라장터 등록

[generate_docx.py 특징]
- python-docx 기반 (한글 UTF-8 완전 지원)
- 검정 박스 없음 (맑은 고딕 직접 적용)
- 편집 가능: Word / 한글(HWP) / LibreOffice
- 섹션별 색상 배너 (파란/빨간/주황 테마)
- 서명란, 첨부서류, 수수료 안내 포함

[certification/source/ 저작권 등록용 소스코드]
- 01_core_ssh_agentless.py  (450줄) - 에이전트리스 SSH 핵심
- 02_core_license_engine.py (455줄) - AES-256-GCM 라이선스
- 03_router_sr_management.py(501줄) - SR 관리 API
- 04_core_ai_classifier.py  (90줄)  - AI 티켓 분류
- 05_frontend_dashboard.js  (200줄) - 대시보드 프론트
- README.md - 제출 안내 및 독창성 설명
- 모든 파일: 영업비밀(암호화키) 마스킹 처리

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 12:33:29 +09:00

668 lines
32 KiB
Python

"""
GUARDiA ITSM 프로그램 등록 신청서 — 3종 개별 PDF 생성기
실행: python generate_each_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_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
# ── 공통 색상 ──────────────────────────────────────────────────
C = {
"navy": colors.HexColor("#1e3a5f"),
"blue": colors.HexColor("#0051A2"),
"lblue": colors.HexColor("#EBF3FB"),
"red": colors.HexColor("#DC2626"),
"lred": colors.HexColor("#FEE2E2"),
"orange": colors.HexColor("#C2410C"),
"lorg": colors.HexColor("#FED7AA"),
"green": colors.HexColor("#065F46"),
"lgrn": colors.HexColor("#D1FAE5"),
"gray": colors.HexColor("#64748B"),
"lgray": colors.HexColor("#F3F4F6"),
"white": colors.white,
"black": colors.black,
"border": colors.HexColor("#CBD5E1"),
}
def S(nm, **kw):
d = dict(fontName=BF, fontSize=9.5, leading=15, textColor=C["black"])
d.update(kw); return ParagraphStyle(nm, **d)
# ── 공통 컴포넌트 ──────────────────────────────────────────────
def banner(main_title, sub_title, accent_color):
"""문서 최상단 컬러 배너."""
data = [
[Paragraph(main_title, S("bt", fontName=BBF, fontSize=20, textColor=C["white"], alignment=TA_CENTER, leading=26))],
[Paragraph(sub_title, S("bs", fontName=BF, fontSize=10, textColor=colors.HexColor("#BDE3FF"), alignment=TA_CENTER))],
]
t = Table(data, colWidths=[WC])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), accent_color),
("TOPPADDING", (0,0), (-1,-1), 16),
("BOTTOMPADDING", (0,0), (-1,-1), 16),
("ROUNDEDCORNERS",(0,0), (-1,-1), [8]),
]))
return t
def sec(title, color):
t = Table([[Paragraph(f" {title}", S("sc", fontName=BBF, fontSize=11, textColor=C["white"], leading=16))]], 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 kv(rows, lbg, cw=None):
"""키-값 테이블."""
if cw is None: cw = [40*mm, WC-40*mm]
cells = []
for r in rows:
if len(r) == 2:
cells.append([Paragraph(r[0], S("kl", fontName=BBF, textColor=C["black"], alignment=TA_CENTER)),
Paragraph(r[1], S("kv"))])
elif len(r) == 3: # 키, 값, 필수여부
cells.append([Paragraph(r[0], S("kl", fontName=BBF, textColor=C["black"], alignment=TA_CENTER)),
Paragraph(r[1], S("kv")),
Paragraph(r[2], S("kr", fontName=BBF, fontSize=8, textColor=C["red"], alignment=TA_CENTER))])
t = Table(cells, colWidths=cw)
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), lbg),
("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, C["border"]),
]))
return t
def empty_kv(rows, lbg, cw=None):
"""입력 란 (빈 칸 강조)."""
if cw is None: cw = [40*mm, WC-40*mm]
cells = [[Paragraph(k, S("ek", fontName=BBF, alignment=TA_CENTER)),
Paragraph(v, S("ev", textColor=C["gray"]))] for k, v in rows]
t = Table(cells, colWidths=cw)
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), lbg),
("FONTSIZE", (0,0), (-1,-1), 9.5),
("ALIGN", (0,0), (0,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 7),
("GRID", (0,0), (-1,-1), 0.5, C["border"]),
]))
return t
def sign_area(date_label="신청일"):
rows = [
[Paragraph(date_label, S("sl", fontName=BBF, alignment=TA_CENTER)),
Paragraph("2026년 월 일", S("sv", fontSize=10.5))],
[Paragraph("회 사 명", S("sl", fontName=BBF, alignment=TA_CENTER)),
Paragraph("(주)지오정보기술", S("sv", fontSize=10.5))],
[Paragraph("대 표 자", S("sl", fontName=BBF, alignment=TA_CENTER)),
Paragraph(" (인)", S("sv", fontSize=10.5))],
]
t = Table(rows, colWidths=[35*mm, WC-35*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), C["lgray"]),
("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, C["black"]),
("GRID", (0,0), (-1,-1), 0.5, C["border"]),
]))
return t
def footer(org_name):
return [
Spacer(1, 8*mm),
HRFlowable(width=WC, color=C["blue"], thickness=1.5),
Spacer(1, 3*mm),
Paragraph(f"{org_name} | (주)지오정보기술 | GUARDiA ITSM v2.0 | Copyright © 2026",
S("ft", fontSize=7.5, textColor=C["gray"], alignment=TA_CENTER, leading=11)),
]
def on_page_num(pname):
def fn(canvas, doc):
canvas.saveState()
canvas.setFont("Helvetica", 7)
canvas.setFillColor(C["gray"])
canvas.drawRightString(W-MARGIN, 9*mm, f"- {doc.page} -")
canvas.drawString(MARGIN, 9*mm, pname)
canvas.restoreState()
return fn
def note(text):
return Paragraph(f"{text}", S("nt", fontSize=8, textColor=C["gray"], leading=12, leftIndent=8))
def make_doc(path, title):
return SimpleDocTemplate(path, pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=MARGIN, bottomMargin=MARGIN,
title=title, author="(주)지오정보기술")
# ══════════════════════════════════════════════════════════════
# PDF 01: 소프트웨어 저작권 등록 신청서
# ══════════════════════════════════════════════════════════════
def make_copyright_pdf(out):
doc = make_doc(out, "소프트웨어 저작권 등록 신청서")
story = []
AC = C["blue"]
LBG = C["lblue"]
story.append(banner(
"소프트웨어 저작권 등록 신청서",
"컴퓨터프로그램저작물 등록 | 한국저작권위원회 | www.copyright.or.kr",
AC
))
story.append(Spacer(1, 5*mm))
story.append(Paragraph("컴퓨터프로그램저작물 등록신청서",
S("h1", fontName=BBF, fontSize=16, textColor=AC, alignment=TA_CENTER, leading=22)))
story.append(Paragraph(
"「저작권법」 제53조 및 「저작권법 시행규칙」 제24조에 따라 다음과 같이 등록을 신청합니다.",
S("ref", fontName=BF, fontSize=9, textColor=C["gray"], alignment=TA_CENTER, leading=14)))
story.append(Spacer(1, 6*mm))
# 1. 저작물 정보
story.append(sec("1. 저작물 정보 (필수 기재)", AC))
story.append(Spacer(1, 3*mm))
story.append(kv([
["저 작 물 명", "GUARDiA ITSM (가이더)"],
["영 문 명", "GUARDiA ITSM Platform"],
["저작물 종류", "컴퓨터프로그램저작물"],
["프로그램 언어","Python 3.11, JavaScript (React 18)"],
["창 작 연 도", "2026년"],
["공 표 여 부", "공표 (2026년 / 인터넷 웹사이트 www.zioinfo.co.kr)"],
["등록 목적", "양도 및 이용허락"],
["버 전", "v2.0.0"],
], LBG))
story.append(Spacer(1, 5*mm))
# 2. 저작자 / 저작권자
story.append(sec("2. 저작자 및 저작권자 정보", AC))
story.append(Spacer(1, 3*mm))
story.append(kv([
["구 분", "법인 저작권자"],
["저 작 자 명", "(주)지오정보기술"],
["저 작 권 자", "(주)지오정보기술 (저작자와 동일)"],
["법인등록번호", "000000-0000000"],
["사업자등록번호", "000-00-00000"],
["주 소", "서울특별시 (상세주소)"],
["대 표 자", "(대표이사명)"],
["전 화", "02-000-0000"],
["이 메 일", "copyright@zioinfo.co.kr"],
], LBG))
story.append(Spacer(1, 5*mm))
# 3. 저작물 설명
story.append(sec("3. 저작물 설명 (200자 이내)", AC))
story.append(Spacer(1, 3*mm))
desc_box = Table([[Paragraph(
"GUARDiA ITSM은 공공기관의 레거시 IT 인프라를 AI로 자율 운영하는 온프레미스 통합 관리 플랫폼입니다. "
"메신저 한 줄 명령으로 에이전트 설치 없이 SSH/SFTP를 통해 WAS 배포·운영을 자동화하며, "
"SR 관리, 인시던트 대응, 변경관리, CMDB, PMS 등 ITSM 전 기능과 "
"AI 자동화(티켓분류·RCA·이상탐지·예측)를 단일 플랫폼에서 제공합니다.",
S("db", leading=16, alignment=TA_JUSTIFY)
)]], colWidths=[WC])
desc_box.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LBG),
("TOPPADDING", (0,0), (-1,-1), 12),
("BOTTOMPADDING", (0,0), (-1,-1), 12),
("LEFTPADDING", (0,0), (-1,-1), 14),
("RIGHTPADDING", (0,0), (-1,-1), 14),
("ROUNDEDCORNERS",(0,0), (-1,-1), [6]),
]))
story.append(desc_box)
story.append(Spacer(1, 5*mm))
# 4. 첨부 서류
story.append(sec("4. 첨부 서류", AC))
story.append(Spacer(1, 3*mm))
attach_h = [["번호", "서류명", "비고"]]
attach_d = [
["1", "저작물 설명서", "본 신청서 3항 활용 가능"],
["2", "소스코드 일부 출력물", "핵심 기능 200줄 이상 (영업비밀 마스킹 후 제출)"],
["3", "법인등기부등본", "최근 3개월 이내 발급본"],
["4", "대리인 위임장", "대리신청 시에만 제출"],
]
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 attach_h+attach_d],
colWidths=[12*mm, 72*mm, WC-84*mm]
)
at.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AC),
("TEXTCOLOR", (0,0), (-1,0), C["white"]),
("FONTNAME", (0,0), (-1,0), BBF),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C["white"], LBG]),
("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, C["border"]),
]))
story.append(at)
story.append(Spacer(1, 5*mm))
# 5. 수수료 안내
story.append(sec("5. 등록 수수료", AC))
story.append(Spacer(1, 3*mm))
fee = Table([
[Paragraph("구분", S("fh", fontName=BBF, alignment=TA_CENTER)),
Paragraph("금액", S("fh", fontName=BBF, alignment=TA_CENTER)),
Paragraph("비고", S("fh", fontName=BBF, alignment=TA_CENTER))],
[Paragraph("컴퓨터프로그램저작물 등록", S("fd")),
Paragraph("40,000원", S("fd", alignment=TA_CENTER)),
Paragraph("온라인 신청 시 카드/계좌이체", S("fd"))],
], colWidths=[70*mm, 30*mm, WC-100*mm])
fee.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AC),
("TEXTCOLOR", (0,0), (-1,0), C["white"]),
("BACKGROUND", (0,1), (-1,1), LBG),
("FONTSIZE", (0,0), (-1,-1), 9.5),
("ALIGN", (1,1), (1,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, C["border"]),
]))
story.append(fee)
story.append(Spacer(1, 3*mm))
story.append(note("신청 방법: 한국저작권위원회 홈페이지(www.copyright.or.kr) → 저작권 등록 → 온라인 신청"))
story.append(note("처리 기간: 약 2주 (보완 요청 시 추가 소요)"))
story.append(Spacer(1, 6*mm))
story.append(Paragraph("위와 같이 컴퓨터프로그램저작물 등록을 신청합니다.",
S("pledge", fontName=BF, fontSize=10, alignment=TA_CENTER, leading=16)))
story.append(Spacer(1, 6*mm))
story.append(sign_area("신 청 일"))
for item in footer("한국저작권위원회 제출용"):
story.append(item)
doc.build(story, onFirstPage=on_page_num("소프트웨어 저작권 등록 신청서 | 한국저작권위원회"),
onLaterPages=on_page_num("소프트웨어 저작권 등록 신청서 | 한국저작권위원회"))
print(f"[OK] 01_소프트웨어_저작권_등록_신청서.pdf ({Path(out).stat().st_size//1024}KB)")
# ══════════════════════════════════════════════════════════════
# PDF 02: 소프트웨어사업자 신고서
# ══════════════════════════════════════════════════════════════
def make_bizreg_pdf(out):
doc = make_doc(out, "소프트웨어사업자 신고서")
story = []
AC = C["red"]
LBG = C["lred"]
story.append(banner(
"소프트웨어사업자 신고서",
"소프트웨어진흥법 제24조 | 과학기술정보통신부 / 한국SW산업협회(KOSA)",
AC
))
story.append(Spacer(1, 5*mm))
story.append(Paragraph("소프트웨어사업자 신고서",
S("h2", fontName=BBF, fontSize=16, textColor=AC, alignment=TA_CENTER, leading=22)))
story.append(Paragraph(
"「소프트웨어진흥법」 제24조 및 같은 법 시행규칙 제14조에 따라 다음과 같이 신고합니다.",
S("ref2", fontName=BF, fontSize=9, textColor=C["gray"], alignment=TA_CENTER, leading=14)))
story.append(Spacer(1, 6*mm))
# 1. 신고인 정보
story.append(sec("1. 신고인(사업자) 정보", AC))
story.append(Spacer(1, 3*mm))
story.append(kv([
["상 호", "(주)지오정보기술"],
["대 표 자", "(대표이사명)"],
["사업자등록번호", "000-00-00000"],
["법인등록번호", "000000-0000000"],
["소 재 지", "서울특별시 (상세주소)"],
["전 화", "02-000-0000"],
["팩 스", "02-000-0001"],
["이 메 일", "sw@zioinfo.co.kr"],
["설 립 일", "2000년 월 일"],
["자 본 금", ""],
["종업원 수", " 명 (정규직 기준)"],
], LBG))
story.append(Spacer(1, 5*mm))
# 2. 소프트웨어사업 종류
story.append(sec("2. 소프트웨어사업의 종류 (해당 항목 모두 선택)", AC))
story.append(Spacer(1, 3*mm))
biz = [
["[V]", "소프트웨어 개발업", "GUARDiA ITSM 등 소프트웨어 개발·판매"],
["[V]", "소프트웨어 공급업", "GUARDiA ITSM 라이선스 공급"],
["[V]", "소프트웨어 유지관리업", "GUARDiA ITSM 기술지원·유지보수"],
["[V]", "소프트웨어 자문·평가·진단업", "IT인프라 컨설팅·진단"],
["[ ]", "정보기술서비스업", ""],
["[ ]", "기타 ( 업)", ""],
]
bh = [["선택", "사업 종류", "세부 내용"]]
bd = [[Paragraph(v, S(f"bv{i}", textColor=AC if "V" in v else C["gray"],
fontName=BBF, fontSize=10, alignment=TA_CENTER)),
Paragraph(n, S(f"bn{i}", fontSize=9.5)),
Paragraph(d, S(f"bd{i}", fontSize=8.5, textColor=C["gray"]))]
for i,(v,n,d) in enumerate(biz)]
bt = Table(bh+bd, colWidths=[16*mm, 65*mm, WC-81*mm])
bt.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AC),
("TEXTCOLOR", (0,0), (-1,0), C["white"]),
("FONTNAME", (0,0), (-1,0), BBF),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C["white"], LBG]),
("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, C["border"]),
]))
story.append(bt)
story.append(Spacer(1, 5*mm))
# 3. 기술인력
story.append(sec("3. 기술인력 현황", AC))
story.append(Spacer(1, 3*mm))
story.append(empty_kv([
["총 직원 수", ""],
["SW 기술인력", " 명 (전체의 %)"],
["정보처리기사", ""],
["정보처리산업기사",""],
["기타 SW 관련 자격", ""],
], LBG))
story.append(Spacer(1, 3*mm))
story.append(note("소프트웨어사업자 신고 기준: 상시근로자 1명 이상 또는 SW기술인력 1명 이상"))
story.append(Spacer(1, 5*mm))
# 4. 주요 제품/서비스
story.append(sec("4. 주요 소프트웨어 제품 및 서비스", AC))
story.append(Spacer(1, 3*mm))
prod_h = [["제품/서비스명", "분류", "출시연도", "연매출(백만원)"]]
prod_d = [
["GUARDiA ITSM", "IT서비스관리 플랫폼", "2026", ""],
["IT인프라 컨설팅", "컨설팅 서비스", "2000", ""],
["SI 구축 서비스", "정보화사업", "2000", ""],
]
ph = Table(prod_h+prod_d, colWidths=[50*mm, 40*mm, 22*mm, WC-112*mm])
ph.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AC),
("TEXTCOLOR", (0,0), (-1,0), C["white"]),
("FONTNAME", (0,0), (-1,0), BBF),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C["white"], LBG]),
("FONTSIZE", (0,0), (-1,-1), 9),
("ALIGN", (0,0), (-1,0), "CENTER"),
("ALIGN", (2,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), 7),
("GRID", (0,0), (-1,-1), 0.5, C["border"]),
]))
story.append(ph)
story.append(Spacer(1, 5*mm))
# 5. SW사업 실적
story.append(sec("5. 소프트웨어사업 실적 (최근 3년)", AC))
story.append(Spacer(1, 3*mm))
perf_h = [["연도", "발주기관", "사업명", "계약금액\n(백만원)", "기간"]]
perf_d = [
["2024", "", "GUARDiA ITSM 구축사업", "", ""],
["2025", "", "인프라 자동화 컨설팅 사업", "", ""],
["2026", "", "GUARDiA ITSM 고도화 사업", "", ""],
]
pt = Table(perf_h+perf_d, colWidths=[14*mm, 40*mm, 58*mm, 22*mm, WC-134*mm])
pt.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AC),
("TEXTCOLOR", (0,0), (-1,0), C["white"]),
("FONTNAME", (0,0), (-1,0), BBF),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C["white"], LBG]),
("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, C["border"]),
]))
story.append(pt)
story.append(Spacer(1, 5*mm))
story.append(note("신고 방법: 한국SW산업협회(swit.or.kr) 온라인 신고 또는 과학기술정보통신부 제출"))
story.append(note("처리 기간: 약 2주 | 수수료: 없음"))
story.append(note("확인서 발급: 신고 완료 후 '소프트웨어사업자 신고확인서' 발급 (GS인증 신청 시 필요)"))
story.append(Spacer(1, 6*mm))
story.append(Paragraph("위와 같이 소프트웨어사업자 신고를 합니다.",
S("pledge2", fontName=BF, fontSize=10, alignment=TA_CENTER, leading=16)))
story.append(Spacer(1, 6*mm))
story.append(sign_area("신 고 일"))
for item in footer("과학기술정보통신부 / 한국SW산업협회(KOSA) 제출용"):
story.append(item)
doc.build(story, onFirstPage=on_page_num("소프트웨어사업자 신고서 | 과학기술정보통신부"),
onLaterPages=on_page_num("소프트웨어사업자 신고서 | 과학기술정보통신부"))
print(f"[OK] 02_소프트웨어사업자_신고서.pdf ({Path(out).stat().st_size//1024}KB)")
# ══════════════════════════════════════════════════════════════
# PDF 03: 조달청 나라장터 물품 등록 신청서
# ══════════════════════════════════════════════════════════════
def make_g2b_pdf(out):
doc = make_doc(out, "조달청 나라장터 물품 등록 신청서")
story = []
AC = C["orange"]
LBG = C["lorg"]
story.append(banner(
"조달청 나라장터 물품 등록 신청서",
"국가종합전자조달시스템 | 조달청 | www.g2b.go.kr",
AC
))
story.append(Spacer(1, 5*mm))
story.append(Paragraph("소프트웨어 물품 등록 신청서 (나라장터)",
S("h3", fontName=BBF, fontSize=16, textColor=AC, alignment=TA_CENTER, leading=22)))
story.append(Paragraph(
"공공기관 납품을 위한 소프트웨어 물품을 국가종합전자조달시스템(나라장터)에 등록합니다.",
S("ref3", fontName=BF, fontSize=9, textColor=C["gray"], alignment=TA_CENTER, leading=14)))
story.append(Spacer(1, 6*mm))
# 1. 공급업체 정보
story.append(sec("1. 공급업체 정보", AC))
story.append(Spacer(1, 3*mm))
story.append(kv([
["업 체 명", "(주)지오정보기술"],
["대 표 자", "(대표이사명)"],
["사업자등록번호", "000-00-00000"],
["소 재 지", "서울특별시 (상세주소)"],
["대 표 전 화", "02-000-0000"],
["팩 스", "02-000-0001"],
["담 당 자", "(담당자명) / 02-000-0000 / g2b@zioinfo.co.kr"],
["계 좌 번 호", "은행명 000-000-000000 (주)지오정보기술"],
], LBG))
story.append(Spacer(1, 5*mm))
# 2. 물품 기본 정보
story.append(sec("2. 물품 기본 정보", AC))
story.append(Spacer(1, 3*mm))
story.append(kv([
["물 품 명", "GUARDiA ITSM (AI 기반 IT서비스관리 플랫폼)"],
["규격 / 모델명", "GUARDiA ITSM v2.0"],
["물품 분류코드", "소프트웨어 > 시스템관리 > IT서비스관리 (ITSM)"],
["원 산 지", "국내산 (대한민국)"],
["제 조 사", "(주)지오정보기술"],
["브 랜 드", "GUARDiA"],
["단 위", "식 (라이선스)"],
["품 목 번 호", "(나라장터 등록 후 부여)"],
], LBG))
story.append(Spacer(1, 5*mm))
# 3. 가격 정보
story.append(sec("3. 가격 정보 (에디션별 라이선스)", AC))
story.append(Spacer(1, 3*mm))
price_h = [["에디션", "대상 규모", "기준 단가 (원)", "연간 유지보수율", "비고"]]
price_d = [
["COMMUNITY", "소규모 / 검토용", "0 (무료)", "", "기관 1개·사용자 10명·서버 20대"],
["STANDARD", "중형 기관 (50기관 이하)", "별도 협의", "15%", "사용자 200명·서버 200대"],
["ENTERPRISE", "대형 관공서 / 광역기관", "별도 협의", "15%", "무제한 (전용 지원 포함)"],
]
pt = Table(price_h+price_d, colWidths=[24*mm, 36*mm, 30*mm, 22*mm, WC-112*mm])
pt.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AC),
("TEXTCOLOR", (0,0), (-1,0), C["white"]),
("FONTNAME", (0,0), (-1,0), BBF),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C["white"], LBG]),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("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, C["border"]),
]))
story.append(pt)
story.append(Spacer(1, 3*mm))
story.append(note("나라장터 등록 단가는 실제 계약 시 협의를 통해 결정됩니다."))
story.append(note("중소기업 직접생산확인증명서 제출 시 중소기업제품 우선구매 적용 가능합니다."))
story.append(Spacer(1, 5*mm))
# 4. 제품 규격서
story.append(sec("4. 제품 규격서 (설치 환경 및 요구 사양)", AC))
story.append(Spacer(1, 3*mm))
story.append(kv([
["운영체제(서버)", "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+ (선택)"],
["AI 엔진", "Ollama 온프레미스 LLM (외부 클라우드 API 미사용)"],
], LBG))
story.append(Spacer(1, 5*mm))
# 5. 주요 기능
story.append(sec("5. 주요 기능 요약", AC))
story.append(Spacer(1, 3*mm))
func_h = [["기능 영역", "주요 기능 내용", "공공기관 필수 여부"]]
func_d = [
["SR / ITSM", "서비스요청 접수·처리, 인시던트, 변경관리, SLA, CMDB", "필수"],
["AI 자동화", "Ollama 온프레미스 AI — 티켓분류, RCA, 이상탐지, 예측", "선택"],
["ChatOps", "카카오워크·네이버웍스·슬랙 25개 봇 명령어 지원", "선택"],
["에이전트리스", "SSH/SFTP 기반 WAS 자동 배포·운영 (서버 설치 불필요)", "필수"],
["PMS", "WBS, 산출물 관리, 일간/주간/월간 보고서 자동 생성", "필수"],
["보안", "JWT+MFA+AES-256+PAM+감사로그+취약점스캔+시큐어코딩", "필수"],
["공공기관 준수", "행안부 체크리스트 19개, 웹접근성(KWCAG 2.1), PIPA", "필수"],
]
ft = Table(func_h+func_d, colWidths=[28*mm, 95*mm, WC-123*mm])
ft.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AC),
("TEXTCOLOR", (0,0), (-1,0), C["white"]),
("FONTNAME", (0,0), (-1,0), BBF),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C["white"], LBG]),
("FONTSIZE", (0,0), (-1,-1), 9),
("ALIGN", (0,0), (-1,0), "CENTER"),
("ALIGN", (0,1), (0,-1), "CENTER"),
("ALIGN", (2,1), (2,-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, C["border"]),
]))
story.append(ft)
story.append(Spacer(1, 5*mm))
# 6. 인증 및 실적
story.append(sec("6. 인증 현황 및 납품 실적", AC))
story.append(Spacer(1, 3*mm))
story.append(kv([
["GS인증(TTA)", "GS 1등급 취득 예정 — 2026년 12월"],
["SW저작권 등록", "C-2026-XXXXXX (등록 후 기입)"],
["SW사업자 신고", "제XXX호 (신고 후 기입)"],
["주요 납품 실적", "(공공기관 납품 실적 기입 — 계약서 사본 첨부)"],
["중소기업 확인", "중소기업 직접생산확인증명서 첨부"],
], LBG))
story.append(Spacer(1, 5*mm))
story.append(note("등록 방법: 조달청 나라장터(g2b.go.kr) → 공급업체 로그인 → 물품 등록 신청"))
story.append(note("필요 서류: 사업자등록증, 법인등기부등본, SW저작권등록증, 제품 규격서, 가격확인서"))
story.append(note("처리 기간: 약 1~2주 | 수수료: 없음"))
story.append(Spacer(1, 6*mm))
story.append(Paragraph("위와 같이 물품 등록을 신청합니다.",
S("pledge3", fontName=BF, fontSize=10, alignment=TA_CENTER, leading=16)))
story.append(Spacer(1, 6*mm))
story.append(sign_area("신 청 일"))
for item in footer("조달청 나라장터 제출용"):
story.append(item)
doc.build(story, onFirstPage=on_page_num("조달청 나라장터 물품 등록 신청서 | 조달청"),
onLaterPages=on_page_num("조달청 나라장터 물품 등록 신청서 | 조달청"))
print(f"[OK] 03_조달청_나라장터_물품_등록_신청서.pdf ({Path(out).stat().st_size//1024}KB)")
# ══════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════
if __name__ == "__main__":
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
base = Path(__file__).parent
print("GUARDiA ITSM 프로그램 등록 신청서 3종 PDF 생성 중...")
print()
make_copyright_pdf(str(base / "01_소프트웨어_저작권_등록_신청서.pdf"))
make_bizreg_pdf( str(base / "02_소프트웨어사업자_신고서.pdf"))
make_g2b_pdf( str(base / "03_조달청_나라장터_물품_등록_신청서.pdf"))
print()
print("3종 PDF 생성 완료:")
for f in sorted(base.glob("0*.pdf")):
print(f" {f.name} ({f.stat().st_size//1024}KB)")