بنقرة واحدة
korean-patent-diagram
// 특허 명세서·출원서 내용을 입력받아 적합한 도면 유형을 자동 분석하고 PNG 파일로 생성하는 스킬. 플로우차트·블록도·상태도·그래프·공정도 지원. KIPO 규격 준수. 변리사·발명자용.
// 특허 명세서·출원서 내용을 입력받아 적합한 도면 유형을 자동 분석하고 PNG 파일로 생성하는 스킬. 플로우차트·블록도·상태도·그래프·공정도 지원. KIPO 규격 준수. 변리사·발명자용.
| name | korean-patent-diagram |
| description | 특허 명세서·출원서 내용을 입력받아 적합한 도면 유형을 자동 분석하고 PNG 파일로 생성하는 스킬. 플로우차트·블록도·상태도·그래프·공정도 지원. KIPO 규격 준수. 변리사·발명자용. |
| license | Apache-2.0 |
| version | 1.1.0 |
────────────────────────────────────────── SpeciAI 🇰🇷 국내 최초·최대 한국 법률 AI 허브 👉 https://discord.gg/3gYGuMcqgb @kimlawtech 에게 피드백과 질문을 남겨주세요! ──────────────────────────────────────────
특허 도면 자동 생성 스킬입니다. 명세서·청구항·아이디어를 입력하면 도면 유형을 분석해 PNG로 만들어 드립니다.
PNG 저장 경로를 알려주세요. (엔터만 누르면 현재 디렉토리에 저장됩니다)
저장 경로: _______________
그 다음 특허 내용을 입력해 주세요. (명세서 전문, 청구항, 발명 아이디어 등 자유롭게 붙여넣기)
도면 유형을 직접 선택하려면 번호를 입력하세요:
| 항목 | 국내 출원 | PCT 국제출원 |
|---|---|---|
| 용지 | A4 (210×297mm) | A4 (210×297mm) |
| 상단 여백 | 40mm | 25mm |
| 좌단 여백 | 25mm | 25mm |
| 하단 여백 | 20mm | 10mm |
| 우단 여백 | 20mm | 15mm |
| 코드 | 유형 | 적합한 특허 |
|---|---|---|
flowchart | 플로우차트 | 소프트웨어, 방법 특허, 알고리즘 |
block | 블록도 | 전자, 통신, 시스템 특허 |
state | 상태도 | 제어 시스템, 프로토콜, UI 특허 |
graph | 그래프 | 성능 비교, 효과 수치, 실험 결과 |
process | 공정도 | 제조, 화학, 생산 공정 특허 |
저장 경로만 먼저 확인한다. 나머지는 기본값으로 진행하고 사용자가 요청 시 조정한다.
SAVE_PATH = 입력값 or 현재 디렉토리 (첫 질문)
DIAGRAM_NO = "도 1" (기본값, 요청 시 변경)
FONT_SIZE = 9pt (기본값, 요청 시 변경)
REF_START = 100 (기본값, 요청 시 변경)
FILING_TYPE = "K" 국내출원 (기본값, PCT 필요 시 변경)
MARGIN = K이면 상40/좌25/하20/우20mm, P이면 상25/좌25/하10/우15mm
TOTAL_PAGES = 도면 생성 직전에 사용자에게 총 몇 개 생성할지 확인 후 확정
사용자가 아래 중 하나를 제공한다:
입력 내용을 분석해 아래 기준으로 도면 유형을 자동 선택한다.
flowchartblockstategraphprocess복합 발명은 여러 도면 유형을 동시에 추천한다.
[분석 결과]
발명 유형: 소프트웨어 / 방법 특허
추천 도면: 플로우차트 (flowchart)
이 도면으로 생성할까요? (Y / 다른 유형 선택)
1) 플로우차트 2) 블록도 3) 상태도 4) 그래프 5) 공정도
총 몇 개의 도면을 생성할 예정인가요? (기본: 1개)
→ 입력값을 TOTAL_PAGES로 확정해 면수 표기("1/N")에 반영한다.
입력 내용에서 구성 요소를 추출하고 참조부호를 자동 부여한다.
구성 요소가 불명확하면 추가 질문한다.
Python 코드를 작성하고 Bash로 실행해 PNG를 생성한다.
공통 생성 규칙:
mkdir -p로 생성{도면유형}_{순번:02d}.png플로우차트 생성 규칙:
블록도 생성 규칙:
상태도 생성 규칙:
그래프 생성 규칙:
공정도 생성 규칙:
[생성 완료]
파일: {SAVE_PATH}/flowchart_01.png
규격: A4 / 300 DPI / 흑백 / 굴림체
여백: 국내출원 기준 (상40/좌25/하20/우20mm)
참조부호: 100번대 적용
추가 도면이 필요하면 말씀해 주세요.
──────────────────────────────────────────
SpeciAI 🇰🇷 국내 최초·최대 한국 법률 AI 허브
특허·계약·노동·투자를 AI로 해결하는
창업자·변리사·변호사 커뮤니티입니다.
도면 품질 피드백, 기능 제안, 질문은
디스코드에서 @kimlawtech 에게 남겨주세요!
👉 https://discord.gg/3gYGuMcqgb
──────────────────────────────────────────
모든 템플릿에 아래 함수를 공통 적용한다:
def set_patent_font():
import os
from matplotlib import font_manager
# 굴림체 우선 — KIPO 도면 표준 폰트
font_paths = [
'C:/Windows/Fonts/gulim.ttc', # Windows 굴림체
'/System/Library/Fonts/Supplemental/AppleGothic.ttf', # macOS 대체
'/System/Library/Fonts/AppleSDGothicNeo.ttc', # macOS SD고딕
'/usr/share/fonts/truetype/nanum/NanumGothic.ttf', # Linux 나눔고딕
'C:/Windows/Fonts/malgun.ttf', # Windows 맑은고딕
]
for fp in font_paths:
if os.path.exists(fp):
font_manager.fontManager.addfont(fp)
prop = font_manager.FontProperties(fname=fp)
matplotlib.rcParams['font.family'] = prop.get_name()
return prop.get_name()
matplotlib.rcParams['font.family'] = 'DejaVu Sans'
return 'DejaVu Sans'
macOS에는 굴림체가 기본 내장되지 않으므로 Apple SD Gothic Neo로 대체한다. Windows 환경에서는 굴림체가 정확히 적용된다.
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.patheffects as pe
import os
def set_patent_font():
from matplotlib import font_manager
font_paths = [
'C:/Windows/Fonts/gulim.ttc',
'/System/Library/Fonts/Supplemental/AppleGothic.ttf',
'/System/Library/Fonts/AppleSDGothicNeo.ttc',
'/usr/share/fonts/truetype/nanum/NanumGothic.ttf',
'C:/Windows/Fonts/malgun.ttf',
]
for fp in font_paths:
if os.path.exists(fp):
font_manager.fontManager.addfont(fp)
prop = font_manager.FontProperties(fname=fp)
matplotlib.rcParams['font.family'] = prop.get_name()
return
matplotlib.rcParams['font.family'] = 'DejaVu Sans'
set_patent_font()
matplotlib.rcParams['axes.unicode_minus'] = False
# ── 설정값 (사용자 입력 반영) ──
FONT_SIZE = 9 # 최소 9pt (KIPO 3.2mm 기준)
DIAGRAM_NO = '도 1'
SAVE_PATH = '.' # 저장 경로
TOTAL_PAGES = 1 # 전체 도면 수
PAGE_NO = 1 # 현재 도면 번호
LINE_W = 1.5 # 외곽선 굵기
SUB_LINE_W = 1.0 # 보조선 굵기
# [공통 헤더 삽입]
# 국내출원 기준 여백 반영 A4
fig, ax = plt.subplots(figsize=(8.27, 11.69))
ax.set_xlim(0, 10)
ax.set_ylim(0, 14)
ax.axis('off')
# 여백 표시 (국내: 상40/좌25/하20/우20mm → 비율 변환)
ax.set_position([0.095, 0.068, 0.810, 0.864])
def ellipse(ax, x, y, w, h, text, fs=None):
fs = fs or FONT_SIZE
e = mpatches.Ellipse((x,y), w, h, lw=LINE_W, edgecolor='black', facecolor='white')
ax.add_patch(e)
ax.text(x, y, text, ha='center', va='center', fontsize=fs, multialignment='center')
def rect(ax, x, y, w, h, text, step=None, ref=None, fs=None):
fs = fs or FONT_SIZE
from matplotlib.patches import FancyBboxPatch
b = FancyBboxPatch((x-w/2, y-h/2), w, h, boxstyle="round,pad=0.05",
lw=LINE_W, edgecolor='black', facecolor='white')
ax.add_patch(b)
label = f"{step}\n{text}" if step else text
ax.text(x, y, label, ha='center', va='center', fontsize=fs, multialignment='center')
if ref:
ax.text(x+w/2-0.05, y-h/2+0.05, str(ref), ha='right', va='bottom', fontsize=fs-2)
def diamond(ax, x, y, w, h, text, ref=None, fs=None):
fs = fs or FONT_SIZE
d = plt.Polygon([[x,y+h/2],[x+w/2,y],[x,y-h/2],[x-w/2,y]],
lw=LINE_W, edgecolor='black', facecolor='white')
ax.add_patch(d)
ax.text(x, y, text, ha='center', va='center', fontsize=fs-1, multialignment='center')
if ref:
ax.text(x+w/2+0.05, y, str(ref), ha='left', va='center', fontsize=fs-2)
def arrow(ax, x1, y1, x2, y2, label='', lx=0.2, ly=0):
ax.annotate('', xy=(x2,y2), xytext=(x1,y1),
arrowprops=dict(arrowstyle='->', color='black', lw=LINE_W))
if label:
ax.text((x1+x2)/2+lx, (y1+y2)/2+ly, label, fontsize=FONT_SIZE-1.5)
def hline(ax, x1, y1, x2, y2):
ax.plot([x1,x2],[y1,y2],'k-', lw=LINE_W)
# ===== 실제 노드와 화살표 코드 삽입 =====
# 도면 번호 (우측 하단)
ax.text(9.8, 0.2, DIAGRAM_NO, ha='right', va='bottom', fontsize=FONT_SIZE)
# 면수 표기 (우측 상단)
ax.text(9.8, 13.8, f'{PAGE_NO}/{TOTAL_PAGES}', ha='right', va='top', fontsize=FONT_SIZE-1)
os.makedirs(SAVE_PATH, exist_ok=True)
plt.savefig(f'{SAVE_PATH}/flowchart_{PAGE_NO:02d}.png', dpi=300,
bbox_inches='tight', facecolor='white', edgecolor='none')
plt.close()
print(f'저장 완료: {SAVE_PATH}/flowchart_{PAGE_NO:02d}.png')
# [공통 헤더 삽입]
fig, ax = plt.subplots(figsize=(11.69, 8.27)) # A4 가로
ax.set_xlim(0, 16)
ax.set_ylim(0, 10)
ax.axis('off')
ax.set_position([0.085, 0.068, 0.830, 0.864])
def block(ax, x, y, w, h, text, ref=None, fs=None, dashed=False):
fs = fs or FONT_SIZE
ls = '--' if dashed else '-'
b = plt.Rectangle((x-w/2, y-h/2), w, h, lw=LINE_W,
edgecolor='black', facecolor='white', linestyle=ls)
ax.add_patch(b)
ax.text(x, y, text, ha='center', va='center', fontsize=fs, multialignment='center')
if ref:
ax.text(x+w/2-0.08, y-h/2+0.08, str(ref), ha='right', va='bottom', fontsize=fs-2)
def arrow(ax, x1, y1, x2, y2, label='', bidir=False):
style = '<->' if bidir else '->'
ax.annotate('', xy=(x2,y2), xytext=(x1,y1),
arrowprops=dict(arrowstyle=style, color='black', lw=LINE_W))
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx, my+0.22, label, ha='center', fontsize=FONT_SIZE-1.5)
def leader_line(ax, x1, y1, x2, y2, ref):
ax.annotate(str(ref), xy=(x1,y1), xytext=(x2,y2),
fontsize=FONT_SIZE-1,
arrowprops=dict(arrowstyle='-', color='black', lw=SUB_LINE_W))
# ===== 실제 블록과 화살표 코드 삽입 =====
ax.text(15.8, 0.2, DIAGRAM_NO, ha='right', va='bottom', fontsize=FONT_SIZE)
ax.text(15.8, 9.8, f'{PAGE_NO}/{TOTAL_PAGES}', ha='right', va='top', fontsize=FONT_SIZE-1)
os.makedirs(SAVE_PATH, exist_ok=True)
plt.savefig(f'{SAVE_PATH}/block_{PAGE_NO:02d}.png', dpi=300,
bbox_inches='tight', facecolor='white', edgecolor='none')
plt.close()
print(f'저장 완료: {SAVE_PATH}/block_{PAGE_NO:02d}.png')
# [공통 헤더 삽입]
fig, ax = plt.subplots(figsize=(8.27, 11.69))
ax.set_xlim(0, 10)
ax.set_ylim(0, 14)
ax.axis('off')
ax.set_position([0.095, 0.068, 0.810, 0.864])
def state(ax, x, y, r, text, ref=None, initial=False, final=False, fs=None):
fs = fs or FONT_SIZE
c = plt.Circle((x,y), r, lw=LINE_W, edgecolor='black', facecolor='white')
ax.add_patch(c)
if initial:
ci = plt.Circle((x,y), r*0.82, lw=LINE_W, edgecolor='black', facecolor='white')
ax.add_patch(ci)
if final:
co = plt.Circle((x,y), r*1.18, lw=2.0, edgecolor='black', facecolor='none')
ax.add_patch(co)
ax.text(x, y, text, ha='center', va='center', fontsize=fs, multialignment='center')
if ref:
ax.text(x+r+0.08, y+r+0.08, str(ref), ha='left', va='bottom', fontsize=fs-2)
def trans(ax, x1, y1, x2, y2, label='', fs=None):
fs = fs or FONT_SIZE-1.5
ax.annotate('', xy=(x2,y2), xytext=(x1,y1),
arrowprops=dict(arrowstyle='->', color='black', lw=LINE_W))
if label:
ax.text((x1+x2)/2+0.2, (y1+y2)/2, label, fontsize=fs)
# ===== 실제 상태와 전이 코드 삽입 =====
ax.text(9.8, 0.2, DIAGRAM_NO, ha='right', va='bottom', fontsize=FONT_SIZE)
ax.text(9.8, 13.8, f'{PAGE_NO}/{TOTAL_PAGES}', ha='right', va='top', fontsize=FONT_SIZE-1)
os.makedirs(SAVE_PATH, exist_ok=True)
plt.savefig(f'{SAVE_PATH}/state_{PAGE_NO:02d}.png', dpi=300,
bbox_inches='tight', facecolor='white', edgecolor='none')
plt.close()
print(f'저장 완료: {SAVE_PATH}/state_{PAGE_NO:02d}.png')
# [공통 헤더 삽입]
fig, ax = plt.subplots(figsize=(8.27, 5.83))
ax.set_position([0.095, 0.12, 0.810, 0.78])
# 흑백 선 스타일 (컬러 금지)
LINE_STYLES = ['-', '--', '-.', ':']
ax.set_xlabel('X축 레이블 (단위)', fontsize=FONT_SIZE)
ax.set_ylabel('Y축 레이블 (단위)', fontsize=FONT_SIZE)
ax.set_title('그래프 제목', fontsize=FONT_SIZE+1)
ax.grid(True, alpha=0.3, linewidth=0.5)
ax.tick_params(labelsize=FONT_SIZE-1)
# ===== 실제 데이터와 플롯 코드 삽입 =====
# 예:
# import numpy as np
# x = np.linspace(0, 10, 100)
# ax.plot(x, y1, LINE_STYLES[0], color='black', lw=1.5, label='본 발명')
# ax.plot(x, y2, LINE_STYLES[1], color='black', lw=1.5, label='비교예')
# ax.legend(fontsize=FONT_SIZE-1)
ax.text(1.0, -0.12, DIAGRAM_NO, transform=ax.transAxes,
ha='right', va='top', fontsize=FONT_SIZE)
ax.text(1.0, 1.02, f'{PAGE_NO}/{TOTAL_PAGES}', transform=ax.transAxes,
ha='right', va='bottom', fontsize=FONT_SIZE-1)
os.makedirs(SAVE_PATH, exist_ok=True)
plt.savefig(f'{SAVE_PATH}/graph_{PAGE_NO:02d}.png', dpi=300,
bbox_inches='tight', facecolor='white', edgecolor='none')
plt.close()
print(f'저장 완료: {SAVE_PATH}/graph_{PAGE_NO:02d}.png')
# [공통 헤더 삽입]
fig, ax = plt.subplots(figsize=(8.27, 11.69))
ax.set_xlim(0, 10)
ax.set_ylim(0, 14)
ax.axis('off')
ax.set_position([0.095, 0.068, 0.810, 0.864])
def proc(ax, x, y, w, h, text, ref=None, fs=None):
fs = fs or FONT_SIZE
b = plt.Rectangle((x-w/2, y-h/2), w, h, lw=LINE_W,
edgecolor='black', facecolor='white')
ax.add_patch(b)
ax.text(x, y, text, ha='center', va='center', fontsize=fs, multialignment='center')
if ref:
ax.text(x+w/2-0.05, y-h/2+0.05, str(ref), ha='right', va='bottom', fontsize=fs-2)
def io_para(ax, x, y, w, h, text, fs=None):
fs = fs or FONT_SIZE
off = 0.35
pts = [[x-w/2+off, y+h/2],[x+w/2+off, y+h/2],
[x+w/2-off, y-h/2],[x-w/2-off, y-h/2]]
p = plt.Polygon(pts, lw=LINE_W, edgecolor='black', facecolor='white')
ax.add_patch(p)
ax.text(x, y, text, ha='center', va='center', fontsize=fs, multialignment='center')
def cond(ax, x, y, w, h, text, ref=None, fs=None):
fs = fs or FONT_SIZE
d = plt.Polygon([[x,y+h/2],[x+w/2,y],[x,y-h/2],[x-w/2,y]],
lw=LINE_W, edgecolor='black', facecolor='white')
ax.add_patch(d)
ax.text(x, y, text, ha='center', va='center', fontsize=fs-1, multialignment='center')
if ref:
ax.text(x+w/2+0.08, y, str(ref), ha='left', va='center', fontsize=fs-2)
def arrow(ax, x1, y1, x2, y2, label='', lx=0.2):
ax.annotate('', xy=(x2,y2), xytext=(x1,y1),
arrowprops=dict(arrowstyle='->', color='black', lw=LINE_W))
if label:
ax.text((x1+x2)/2+lx, (y1+y2)/2, label, fontsize=FONT_SIZE-1.5)
def sideline(ax, x1, y1, x2, y2):
ax.plot([x1,x2],[y1,y2],'k-', lw=LINE_W)
# ===== 실제 공정 단계 코드 삽입 =====
ax.text(9.8, 0.2, DIAGRAM_NO, ha='right', va='bottom', fontsize=FONT_SIZE)
ax.text(9.8, 13.8, f'{PAGE_NO}/{TOTAL_PAGES}', ha='right', va='top', fontsize=FONT_SIZE-1)
os.makedirs(SAVE_PATH, exist_ok=True)
plt.savefig(f'{SAVE_PATH}/process_{PAGE_NO:02d}.png', dpi=300,
bbox_inches='tight', facecolor='white', edgecolor='none')
plt.close()
print(f'저장 완료: {SAVE_PATH}/process_{PAGE_NO:02d}.png')
| 오류 | 조치 |
|---|---|
ModuleNotFoundError: matplotlib | pip3 install matplotlib 안내 |
| 굴림체 없음 | macOS: Apple SD Gothic Neo 자동 대체 / Linux: 나눔고딕 |
| 저장 경로 없음 | mkdir -p로 자동 생성 |
| 글자 크기 9pt 미만 입력 | 9pt로 강제 상향 후 경고 출력 |
| 구성요소 불명확 | 추가 질문 후 재시도 |