feat(cert): 프로그램 등록 신청서 통합 PDF 생성 (4페이지, 3종 신청서)
This commit is contained in:
parent
0854a0fea2
commit
5005078b3e
633
certification/generate_registration_pdf.py
Normal file
633
certification/generate_registration_pdf.py
Normal file
@ -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))
|
||||||
BIN
certification/프로그램등록_신청서_GUARDiA_ITSM_v2.0.pdf
Normal file
BIN
certification/프로그램등록_신청서_GUARDiA_ITSM_v2.0.pdf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user