|
import gradio as gr |
|
import pandas as pd |
|
import io |
|
import re |
|
from difflib import get_close_matches |
|
from datetime import datetime |
|
import tempfile |
|
import os |
|
|
|
|
|
FONT_LICENSE_DB = { |
|
"나눔고딕": { |
|
"license": "SIL OFL 1.1", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "네이버", |
|
"provider_url": "https://hangeul.naver.com/2017/nanum", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"나눔바른고딕": { |
|
"license": "SIL OFL 1.1", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "네이버", |
|
"provider_url": "https://hangeul.naver.com/2017/nanum", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"나눔명조": { |
|
"license": "SIL OFL 1.1", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "네이버", |
|
"provider_url": "https://hangeul.naver.com/2017/nanum", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"나눔손글씨": { |
|
"license": "SIL OFL 1.1", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "네이버", |
|
"provider_url": "https://hangeul.naver.com/2017/nanum", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"나눔펜": { |
|
"license": "SIL OFL 1.1", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "네이버", |
|
"provider_url": "https://hangeul.naver.com/2017/nanum", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"배달의민족 주아": { |
|
"license": "커스텀 무료 라이선스", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "불가능", |
|
"provider": "배달의민족", |
|
"provider_url": "https://www.woowahan.com/fonts", |
|
"notes": "상업적 사용 가능하나 BI 제작 시 사용 금지, 폰트 자체 판매 금지" |
|
}, |
|
"배달의민족 도현": { |
|
"license": "커스텀 무료 라이선스", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "불가능", |
|
"provider": "배달의민족", |
|
"provider_url": "https://www.woowahan.com/fonts", |
|
"notes": "상업적 사용 가능하나 BI 제작 시 사용 금지, 폰트 자체 판매 금지" |
|
}, |
|
"배달의민족 기랑해랑": { |
|
"license": "커스텀 무료 라이선스", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "불가능", |
|
"provider": "배달의민족", |
|
"provider_url": "https://www.woowahan.com/fonts", |
|
"notes": "상업적 사용 가능하나 BI 제작 시 사용 금지, 폰트 자체 판매 금지" |
|
}, |
|
"서울남산체": { |
|
"license": "SIL OFL 1.1", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "서울시", |
|
"provider_url": "https://www.seoul.go.kr/solution/font.do", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"서울한강체": { |
|
"license": "SIL OFL 1.1", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "서울시", |
|
"provider_url": "https://www.seoul.go.kr/solution/font.do", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"Noto Sans Korean": { |
|
"license": "SIL OFL 1.1", |
|
"display_name": "노토 산스 한국어", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "Google", |
|
"provider_url": "https://fonts.google.com/noto", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"Source Han Sans Korean": { |
|
"license": "SIL OFL 1.1", |
|
"display_name": "본고딕", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "Adobe/Google", |
|
"provider_url": "https://fonts.google.com/noto", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"Pretendard": { |
|
"license": "SIL OFL 1.1", |
|
"display_name": "프리텐다드", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "Kil Hyung-jin", |
|
"provider_url": "https://github.com/orioncactus/pretendard", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"SUIT": { |
|
"license": "SIL OFL 1.1", |
|
"display_name": "수트", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "SUNN", |
|
"provider_url": "https://github.com/sunn-us/SUIT", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"IBM Plex Sans Korean": { |
|
"license": "SIL OFL 1.1", |
|
"display_name": "IBM 플렉스 산스 한국어", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "IBM", |
|
"provider_url": "https://fonts.google.com/specimen/IBM+Plex+Sans+KR", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"Spoqa Han Sans": { |
|
"license": "SIL OFL 1.1", |
|
"display_name": "스포카 한 산스", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "스포카", |
|
"provider_url": "https://github.com/spoqa/spoqa-han-sans", |
|
"notes": "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요" |
|
}, |
|
"NEXON Lv1 Gothic": { |
|
"license": "커스텀 무료 라이선스", |
|
"display_name": "넥슨 레벨1 고딕", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "불가능", |
|
"provider": "넥슨", |
|
"provider_url": "https://www.nexon.com/Home/Game/font", |
|
"notes": "상업적 사용 가능, 폰트 자체 판매 금지" |
|
}, |
|
"NEXON Lv2 Gothic": { |
|
"license": "커스텀 무료 라이선스", |
|
"display_name": "넥슨 레벨2 고딕", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "불가능", |
|
"provider": "넥슨", |
|
"provider_url": "https://www.nexon.com/Home/Game/font", |
|
"notes": "상업적 사용 가능, 폰트 자체 판매 금지" |
|
}, |
|
"TmoneyRoundWind": { |
|
"license": "커스텀 무료 라이선스", |
|
"display_name": "티머니 라운드윈드", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "불가능", |
|
"provider": "티몬", |
|
"provider_url": "https://brunch.co.kr/@creative/32", |
|
"notes": "상업적 사용 가능, 폰트 자체 판매 금지" |
|
}, |
|
"한국관광공사체": { |
|
"license": "공공누리 제1유형", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "불필요", |
|
"modification": "가능", |
|
"provider": "한국관광공사", |
|
"provider_url": "https://kto.visitkorea.or.kr/kor/notice/data/storybook/font.kto", |
|
"notes": "공공누리 제1유형: 출처표시 조건으로 자유이용 가능" |
|
}, |
|
"KoPubWorld돋움체": { |
|
"license": "공공누리 제1유형", |
|
"commercial_free": "✅ 가능", |
|
"attribution": "필요", |
|
"modification": "가능", |
|
"provider": "한국출판인쇄문화협회", |
|
"provider_url": "http://www.kopus.org/biz/electronic/font.aspx", |
|
"notes": "공공누리 제1유형: 출처표시 조건으로 자유이용 가능" |
|
}, |
|
"윤고딕": { |
|
"license": "상업적 라이선스 필요", |
|
"commercial_free": "❌ 유료", |
|
"attribution": "해당없음", |
|
"modification": "라이선스에 따라", |
|
"provider": "윤디자인그룹", |
|
"provider_url": "https://www.yoondesign.com", |
|
"notes": "개인 사용만 무료, 상업적 사용 시 유료 라이선스 구매 필요" |
|
}, |
|
"윤명조": { |
|
"license": "상업적 라이선스 필요", |
|
"commercial_free": "❌ 유료", |
|
"attribution": "해당없음", |
|
"modification": "라이선스에 따라", |
|
"provider": "윤디자인그룹", |
|
"provider_url": "https://www.yoondesign.com", |
|
"notes": "개인 사용만 무료, 상업적 사용 시 유료 라이선스 구매 필요" |
|
}, |
|
"산돌고딕": { |
|
"license": "상업적 라이선스 필요", |
|
"commercial_free": "❌ 유료", |
|
"attribution": "해당없음", |
|
"modification": "라이선스에 따라", |
|
"provider": "산돌커뮤니케이션", |
|
"provider_url": "https://www.sandoll.co.kr", |
|
"notes": "개인 사용만 무료, 상업적 사용 시 유료 라이선스 구매 필요" |
|
}, |
|
"산돌명조": { |
|
"license": "상업적 라이선스 필요", |
|
"commercial_free": "❌ 유료", |
|
"attribution": "해당없음", |
|
"modification": "라이선스에 따라", |
|
"provider": "산돌커뮤니케이션", |
|
"provider_url": "https://www.sandoll.co.kr", |
|
"notes": "개인 사용만 무료, 상업적 사용 시 유료 라이선스 구매 필요" |
|
} |
|
} |
|
|
|
def clean_font_name(font_name): |
|
"""폰트 이름 정리""" |
|
font_name = re.sub(r'\.(ttf|otf|ttc|woff|woff2)$', '', font_name, flags=re.IGNORECASE) |
|
patterns = [ |
|
r'(.+?)[-_\s]?(Regular|Bold|Light|Medium|Thin|Black|Heavy)', |
|
r'(.+?)[-_\s]?\d+', |
|
r'(.+?)[-_\s]?(Kr|KR|Korean)', |
|
] |
|
|
|
cleaned = font_name.strip() |
|
for pattern in patterns: |
|
match = re.search(pattern, cleaned, re.IGNORECASE) |
|
if match: |
|
cleaned = match.group(1).strip() |
|
break |
|
|
|
cleaned = re.sub(r'[-_]+', ' ', cleaned).strip() |
|
return cleaned |
|
|
|
def find_font_license(font_name): |
|
"""폰트 라이선스 정보 찾기 - 한글명 우선 표시""" |
|
cleaned_name = clean_font_name(font_name) |
|
|
|
|
|
if cleaned_name in FONT_LICENSE_DB: |
|
info = FONT_LICENSE_DB[cleaned_name].copy() |
|
|
|
if 'display_name' in info: |
|
info['display_font_name'] = info['display_name'] |
|
else: |
|
info['display_font_name'] = cleaned_name |
|
return info |
|
|
|
|
|
for db_font, info in FONT_LICENSE_DB.items(): |
|
if db_font.lower() in cleaned_name.lower() or cleaned_name.lower() in db_font.lower(): |
|
info_copy = info.copy() |
|
if 'display_name' in info_copy: |
|
info_copy['display_font_name'] = info_copy['display_name'] |
|
else: |
|
info_copy['display_font_name'] = db_font |
|
return info_copy |
|
|
|
|
|
special_patterns = { |
|
r'nanum|나눔': "나눔고딕", |
|
r'baemin|배민|배달의민족': "배달의민족 주아", |
|
r'seoul|서울': "서울남산체", |
|
r'pretendard': "Pretendard", |
|
r'noto.*sans.*kr|noto.*sans.*korean': "Noto Sans Korean", |
|
r'source.*han.*sans|본고딕': "Source Han Sans Korean", |
|
r'ibm.*plex.*sans.*kr|ibm.*plex.*sans.*korean': "IBM Plex Sans Korean", |
|
r'spoqa.*han.*sans': "Spoqa Han Sans", |
|
r'nexon.*lv1|nexon.*level.*1': "NEXON Lv1 Gothic", |
|
r'nexon.*lv2|nexon.*level.*2': "NEXON Lv2 Gothic", |
|
r'tmoney.*round.*wind': "TmoneyRoundWind", |
|
r'suit': "SUIT", |
|
r'yoon|윤': "윤고딕", |
|
r'sandoll|산돌': "산돌고딕", |
|
r'kopub.*world': "KoPubWorld돋움체", |
|
r'한국관광공사': "한국관광공사체" |
|
} |
|
|
|
for pattern, matched_font in special_patterns.items(): |
|
if re.search(pattern, cleaned_name.lower()): |
|
if matched_font in FONT_LICENSE_DB: |
|
info_copy = FONT_LICENSE_DB[matched_font].copy() |
|
if 'display_name' in info_copy: |
|
info_copy['display_font_name'] = info_copy['display_name'] |
|
else: |
|
info_copy['display_font_name'] = matched_font |
|
return info_copy |
|
|
|
|
|
matches = get_close_matches(cleaned_name, FONT_LICENSE_DB.keys(), n=1, cutoff=0.6) |
|
if matches: |
|
info_copy = FONT_LICENSE_DB[matches[0]].copy() |
|
if 'display_name' in info_copy: |
|
info_copy['display_font_name'] = info_copy['display_name'] |
|
else: |
|
info_copy['display_font_name'] = matches[0] |
|
return info_copy |
|
|
|
|
|
return { |
|
"license": "❓ 확인 필요", |
|
"commercial_free": "❓ 확인 필요", |
|
"attribution": "❓ 확인 필요", |
|
"modification": "❓ 확인 필요", |
|
"provider": "❓ 확인 필요", |
|
"provider_url": "", |
|
"display_font_name": cleaned_name, |
|
"notes": "라이선스 정보를 찾을 수 없습니다. 제작사 공식 사이트에서 확인하세요." |
|
} |
|
|
|
def parse_font_list(file_content): |
|
"""폰트 목록 파싱""" |
|
try: |
|
if isinstance(file_content, bytes): |
|
encodings = ['utf-8', 'cp949', 'euc-kr', 'latin1'] |
|
for encoding in encodings: |
|
try: |
|
file_content = file_content.decode(encoding) |
|
break |
|
except: |
|
continue |
|
|
|
lines = file_content.strip().split('\n') |
|
fonts = [] |
|
for line in lines: |
|
line = line.strip() |
|
if line and not line.startswith('#'): |
|
fonts.append(line) |
|
|
|
return fonts |
|
except Exception as e: |
|
return [f"파일 파싱 오류: {str(e)}"] |
|
|
|
def analyze_fonts(file_content): |
|
"""폰트 분석""" |
|
try: |
|
font_list = parse_font_list(file_content) |
|
|
|
if not font_list or font_list[0].startswith("파일 파싱 오류"): |
|
return None, "파일을 읽을 수 없습니다." |
|
|
|
results = [] |
|
for font_file in font_list: |
|
font_name = clean_font_name(font_file) |
|
license_info = find_font_license(font_name) |
|
|
|
|
|
display_name = license_info.get('display_font_name', font_name) |
|
|
|
results.append({ |
|
"원본 파일명": font_file, |
|
"폰트명": display_name, |
|
"라이선스": license_info["license"], |
|
"상업적 사용": license_info["commercial_free"], |
|
"출처 표시": license_info["attribution"], |
|
"수정 가능": license_info["modification"], |
|
"제공처": license_info["provider"], |
|
"제공처 URL": license_info["provider_url"], |
|
"비고": license_info["notes"] |
|
}) |
|
|
|
df = pd.DataFrame(results) |
|
|
|
total_fonts = len(results) |
|
commercial_free = len([r for r in results if "✅" in r["상업적 사용"]]) |
|
needs_check = len([r for r in results if "❓" in r["상업적 사용"]]) |
|
commercial_paid = len([r for r in results if "❌" in r["상업적 사용"]]) |
|
|
|
summary = f""" |
|
## 📊 분석 결과 요약 |
|
|
|
- **총 폰트 수**: {total_fonts}개 |
|
- **상업적 무료**: {commercial_free}개 ({commercial_free/total_fonts*100:.1f}%) |
|
- **유료 라이선스**: {commercial_paid}개 ({commercial_paid/total_fonts*100:.1f}%) |
|
- **확인 필요**: {needs_check}개 ({needs_check/total_fonts*100:.1f}%) |
|
|
|
✅ **엑셀 파일이 준비되었습니다!** |
|
""" |
|
|
|
return df, summary |
|
|
|
except Exception as e: |
|
return None, f"분석 중 오류: {str(e)}" |
|
|
|
def create_excel_download(df): |
|
"""엑셀 파일 생성""" |
|
try: |
|
output = io.BytesIO() |
|
|
|
with pd.ExcelWriter(output, engine='openpyxl') as writer: |
|
df.to_excel(writer, sheet_name='폰트 라이선스 정보', index=False) |
|
|
|
worksheet = writer.sheets['폰트 라이선스 정보'] |
|
column_widths = { |
|
'A': 25, 'B': 20, 'C': 25, 'D': 15, |
|
'E': 12, 'F': 12, 'G': 20, 'H': 40, 'I': 50 |
|
} |
|
|
|
for col, width in column_widths.items(): |
|
worksheet.column_dimensions[col].width = width |
|
|
|
output.seek(0) |
|
return output.getvalue() |
|
|
|
except Exception as e: |
|
return None |
|
|
|
def process_font_file(file): |
|
"""파일 처리""" |
|
if file is None: |
|
return None, "파일을 업로드해주세요.", None |
|
|
|
try: |
|
if hasattr(file, 'read'): |
|
content = file.read() |
|
else: |
|
with open(file, 'rb') as f: |
|
content = f.read() |
|
|
|
df, summary = analyze_fonts(content) |
|
|
|
if df is None: |
|
return None, summary, None |
|
|
|
excel_data = create_excel_download(df) |
|
|
|
if excel_data is None: |
|
return df, summary, None |
|
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
excel_filename = f"font_license_analysis_{timestamp}.xlsx" |
|
temp_dir = tempfile.gettempdir() |
|
excel_path = os.path.join(temp_dir, excel_filename) |
|
|
|
with open(excel_path, 'wb') as f: |
|
f.write(excel_data) |
|
|
|
return df, summary, excel_path |
|
|
|
except Exception as e: |
|
return None, f"파일 처리 오류: {str(e)}", None |
|
|
|
def create_app(): |
|
with gr.Blocks(title="한국어 폰트 라이선스 분석기") as app: |
|
|
|
gr.Markdown(""" |
|
# 🔍 한국어 폰트 라이선스 분석기 |
|
|
|
**폰트 목록을 업로드하면 라이선스 정보를 분석하여 엑셀로 제공합니다!** |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
gr.Markdown("## 📁 폰트 목록 파일 업로드") |
|
|
|
file_input = gr.File( |
|
label="폰트 목록 텍스트 파일 (.txt)", |
|
file_types=[".txt"] |
|
) |
|
|
|
analyze_btn = gr.Button("🔍 라이선스 분석 시작", variant="primary") |
|
|
|
gr.Markdown(""" |
|
### 사용 방법 |
|
1. 메모장에서 폰트 파일명을 한 줄에 하나씩 입력 |
|
2. txt 파일로 저장 후 업로드 |
|
3. 분석 결과 확인 및 엑셀 다운로드 |
|
|
|
**예시 파일 내용:** |
|
``` |
|
NanumGothic.ttf |
|
Pretendard-Regular.otf |
|
BMDOHYEON_ttf.ttf |
|
``` |
|
""") |
|
|
|
with gr.Column(scale=2): |
|
gr.Markdown("## 📊 분석 결과") |
|
|
|
summary_output = gr.Markdown("파일을 업로드하세요.") |
|
|
|
result_table = gr.Dataframe( |
|
headers=["원본 파일명", "폰트명", "라이선스", "상업적 사용", "출처 표시", "수정 가능", "제공처", "제공처 URL", "비고"], |
|
label="폰트 라이선스 정보", |
|
column_widths=["15%", "12%", "15%", "10%", "8%", "8%", "12%", "20%", "20%"], |
|
interactive=True |
|
) |
|
|
|
excel_download = gr.File(label="📥 엑셀 다운로드", visible=False) |
|
|
|
with gr.Accordion("📋 샘플 결과", open=False): |
|
sample_data = [ |
|
["NanumGothic.ttf", "나눔고딕", "SIL OFL 1.1", "✅ 가능", "불필요", "가능", "네이버", "https://hangeul.naver.com/2017/nanum", "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요"], |
|
["Pretendard-Regular.otf", "프리텐다드", "SIL OFL 1.1", "✅ 가능", "불필요", "가능", "Kil Hyung-jin", "https://github.com/orioncactus/pretendard", "OFL 1.1 라이선스로 상업적 사용 자유, 출처 표시 불필요"], |
|
["YoonGothic.ttf", "윤고딕", "상업적 라이선스 필요", "❌ 유료", "해당없음", "라이선스에 따라", "윤디자인그룹", "https://www.yoondesign.com", "개인 사용만 무료, 상업적 사용 시 유료 라이선스 구매 필요"], |
|
["KoPubWorldDotum.ttf", "KoPubWorld돋움체", "공공누리 제1유형", "✅ 가능", "필요", "가능", "한국출판인쇄문화협회", "http://www.kopus.org/biz/electronic/font.aspx", "공공누리 제1유형: 출처표시 조건으로 자유이용 가능"] |
|
] |
|
|
|
gr.Dataframe( |
|
value=sample_data, |
|
headers=["원본 파일명", "폰트명", "라이선스", "상업적 사용", "출처 표시", "수정 가능", "제공처", "제공처 URL", "비고"], |
|
column_widths=["15%", "12%", "15%", "10%", "8%", "8%", "12%", "20%", "20%"] |
|
) |
|
|
|
with gr.Accordion("ℹ️ 라이선스 유형 설명", open=False): |
|
gr.Markdown(""" |
|
### 주요 라이선스 유형 (재검증됨) |
|
|
|
**✅ SIL OFL 1.1 (Open Font License)** |
|
- 상업적 사용: 완전 자유 |
|
- 출처 표시: 불필요 |
|
- 수정/재배포: 가능 |
|
- 웹폰트 사용: 가능 |
|
|
|
**✅ 커스텀 무료 라이선스** |
|
- 각 제작사별 고유 조건 |
|
- 대부분 상업적 사용 가능 |
|
- 일부 제한사항 존재 (BI 사용 금지 등) |
|
|
|
**✅ 공공누리 제1유형** |
|
- 공공기관에서 제작한 폰트 |
|
- 출처표시 조건으로 자유이용 가능 |
|
- 상업적 사용 가능 |
|
|
|
**❌ 상업적 라이선스 필요** |
|
- 개인 사용: 무료 |
|
- 상업적 사용: 유료 라이선스 구매 필요 |
|
- 주로 전문 폰트 제작사 폰트들 |
|
|
|
**❓ 확인 필요** |
|
- 데이터베이스에 정보가 없는 폰트 |
|
- 제작사 공식 사이트에서 직접 확인 필요 |
|
""") |
|
|
|
gr.Markdown("### ⚠️ 안내사항") |
|
gr.Markdown("- 참고용 도구입니다. 상업적 사용 전 공식 사이트에서 최종 확인하세요.") |
|
gr.Markdown("- 라이선스 정보는 재검증되었으나 변경될 수 있습니다.") |
|
gr.Markdown("- 총 25개 주요 한국어 폰트 정보를 제공합니다.") |
|
|
|
def handle_analysis(file): |
|
if file is None: |
|
return "파일을 업로드해주세요.", None, gr.File(visible=False) |
|
|
|
df, summary, excel_file = process_font_file(file) |
|
|
|
if df is None: |
|
return summary, None, gr.File(visible=False) |
|
|
|
if excel_file: |
|
return summary, df, gr.File(value=excel_file, visible=True) |
|
else: |
|
return summary, df, gr.File(visible=False) |
|
|
|
analyze_btn.click( |
|
fn=handle_analysis, |
|
inputs=file_input, |
|
outputs=[summary_output, result_table, excel_download] |
|
) |
|
|
|
file_input.change( |
|
fn=handle_analysis, |
|
inputs=file_input, |
|
outputs=[summary_output, result_table, excel_download] |
|
) |
|
|
|
return app |
|
|
|
if __name__ == "__main__": |
|
app = create_app() |
|
app.launch() |