Skip to main content
Glama

MCP Sheet Parser

by yuqie6
MIT License
3
  • Apple
font_manager.py12.6 kB
""" 智能字体管理器 自动处理各种字体,无需手动修改代码。 支持字体配置文件、自动检测、智能回退等功能。 """ import json import re from pathlib import Path import logging logger = logging.getLogger(__name__) class FontManager: """ 智能字体管理器 功能: 1. 自动检测字体类型(中文、英文、等宽、衬线等) 2. 智能生成回退字体 3. 支持配置文件自定义 4. 自动学习新字体 """ def __init__(self, config_file: str | None = None): """ 初始化字体管理器 参数: config_file: 字体配置文件路径 """ self.config_file = config_file or self._get_default_config_path() config_data = self._load_config_from_file() self.font_database = self._load_font_database(config_data) self.custom_mappings = self._load_custom_mappings(config_data) def _get_default_config_path(self) -> str: """获取默认配置文件路径""" config_dir = Path(__file__).parent / "config" config_dir.mkdir(exist_ok=True) return str(config_dir / "font_config.json") def _load_config_from_file(self) -> dict: """从文件加载配置""" try: if Path(self.config_file).exists(): with open(self.config_file, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: logger.warning(f"加载字体配置文件失败: {e}") return {} def _load_font_database(self, config_data: dict) -> dict: """加载字体数据库""" default_database = { "chinese_keywords": [ # 中文字体关键词 "华文", "微软", "宋体", "黑体", "楷体", "仿宋", "隶书", "行楷", "方正", "汉仪", "文泉驿", "思源", "Source Han", "Noto", "SimSun", "SimHei", "SimKai", "FangSong", "Microsoft YaHei", "PingFang", "Hiragino", "Yu Gothic", "Meiryo" ], "monospace_keywords": [ # 等宽字体关键词 "Courier", "Consolas", "Monaco", "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Source Code Pro", "Fira Code", "JetBrains Mono", "Cascadia Code", "SF Mono", "Roboto Mono" ], "serif_keywords": [ # 衬线字体关键词 "Times", "Georgia", "Garamond", "Book Antiqua", "Palatino", "Baskerville", "Minion", "Caslon", "Trajan", "Optima" ], "sans_serif_keywords": [ # 无衬线字体关键词 "Arial", "Helvetica", "Verdana", "Tahoma", "Calibri", "Segoe UI", "Open Sans", "Roboto", "Lato", "Montserrat" ], "fallback_chains": { "chinese": [ "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Noto Sans CJK SC", "SimHei", "SimSun", "sans-serif" ], "monospace": [ "Consolas", "Monaco", "Courier New", "Liberation Mono", "DejaVu Sans Mono", "monospace" ], "serif": [ "Times New Roman", "Georgia", "Times", "serif" ], "sans_serif": [ "Arial", "Helvetica", "Segoe UI", "sans-serif" ] } } # 合并默认配置和用户配置 for key, value in config_data.get('font_database', {}).items(): if key in default_database: if isinstance(value, list): default_database[key].extend(value) elif isinstance(value, dict): default_database[key].update(value) return default_database def _load_custom_mappings(self, config_data: dict) -> dict[str, str]: """加载自定义字体映射""" return config_data.get('custom_mappings', {}) def detect_font_type(self, font_name: str) -> str: """ 自动检测字体类型 参数: font_name: 字体名称 返回: 字体类型: 'chinese', 'monospace', 'serif', 'sans_serif' """ if not font_name: return 'sans_serif' font_lower = font_name.lower() # 检查是否为中文字体 for keyword in self.font_database['chinese_keywords']: if keyword.lower() in font_lower: return 'chinese' # 检查是否包含中文字符 if any(ord(c) > 127 for c in font_name): return 'chinese' # 检查是否为等宽字体 for keyword in self.font_database['monospace_keywords']: if keyword.lower() in font_lower: return 'monospace' # 检查是否为衬线字体 for keyword in self.font_database['serif_keywords']: if keyword.lower() in font_lower: return 'serif' # 默认为无衬线字体 return 'sans_serif' def needs_quotes(self, font_name: str) -> bool: """ 判断字体名称是否需要引号包围 参数: font_name: 字体名称 返回: 是否需要引号 """ if not font_name: return False # 包含空格、中文字符或特殊字符时需要引号 return (" " in font_name or any(ord(c) > 127 for c in font_name) or any(c in font_name for c in [',', ';', '(', ')', '[', ']', '"', "'", '-', '_'])) def format_font_name(self, font_name: str) -> str: """ 格式化字体名称 参数: font_name: 原始字体名称 返回: 格式化后的字体名称 """ if not font_name: return "" # 清理字体名称 cleaned_name = font_name.strip() # 检查自定义映射 if cleaned_name in self.custom_mappings: cleaned_name = self.custom_mappings[cleaned_name] # 添加引号(如果需要) if self.needs_quotes(cleaned_name): return f'"{cleaned_name}"' else: return cleaned_name def get_fallback_fonts(self, font_type: str) -> list[str]: """ 获取回退字体列表 参数: font_type: 字体类型 返回: 回退字体列表 """ return self.font_database['fallback_chains'].get(font_type, self.font_database['fallback_chains']['sans_serif']) def generate_font_family(self, font_name: str) -> str: """ 生成完整的font-family字符串 参数: font_name: 主字体名称 返回: 完整的font-family字符串 """ if not font_name: return ', '.join(self.get_fallback_fonts('sans_serif')) # 格式化主字体名称 formatted_name = self.format_font_name(font_name) # 检测字体类型 font_type = self.detect_font_type(font_name) # 获取回退字体 fallback_fonts = self.get_fallback_fonts(font_type) # 组合字体列表 font_list = [formatted_name] + fallback_fonts return ', '.join(font_list) def learn_font(self, font_name: str, font_type: str) -> None: """ 学习新字体(添加到数据库) 参数: font_name: 字体名称 font_type: 字体类型 """ if not font_name or font_type not in self.font_database['fallback_chains']: return # 提取字体关键词 keywords = self._extract_keywords(font_name) # 添加到对应类型的关键词列表 type_key = f"{font_type}_keywords" if type_key in self.font_database: for keyword in keywords: if keyword not in self.font_database[type_key]: self.font_database[type_key].append(keyword) logger.info(f"学习新字体关键词: {keyword} -> {font_type}") def _extract_keywords(self, font_name: str) -> list[str]: """ 从字体名称中提取关键词 参数: font_name: 字体名称 返回: 关键词列表 """ keywords = [] # 按空格分割 words = font_name.split() for word in words: if len(word) > 1: # 忽略单字符 keywords.append(word) # 提取中文词汇 chinese_pattern = r'[\u4e00-\u9fff]+' chinese_words = re.findall(chinese_pattern, font_name) keywords.extend(chinese_words) return keywords def save_config(self) -> None: """保存配置到文件""" try: config_data = { 'font_database': self.font_database, 'custom_mappings': self.custom_mappings } # 确保配置目录存在 config_path = Path(self.config_file) config_path.parent.mkdir(parents=True, exist_ok=True) # 使用临时文件确保原子性写入 temp_file = config_path.with_suffix('.tmp') try: with open(temp_file, 'w', encoding='utf-8') as f: json.dump(config_data, f, ensure_ascii=False, indent=2) # 原子性地替换原文件 temp_file.replace(config_path) logger.info(f"字体配置已保存到: {self.config_file}") except Exception as e: # 清理临时文件 if temp_file.exists(): temp_file.unlink() raise e except (OSError, TypeError) as e: logger.error(f"保存字体配置失败: {e}") except Exception as e: logger.error(f"保存字体配置时发生未预期的错误: {e}") def add_custom_mapping(self, original_name: str, mapped_name: str) -> None: """ 添加自定义字体映射 参数: original_name: 原始字体名称 mapped_name: 映射后的字体名称 """ self.custom_mappings[original_name] = mapped_name logger.info(f"添加字体映射: {original_name} -> {mapped_name}") def get_font_info(self, font_name: str) -> dict: """ 获取字体的详细信息 参数: font_name: 字体名称 返回: 字体信息字典 """ font_type = self.detect_font_type(font_name) # formatted_name应该是格式化后的原始名称,不应用映射 formatted_name = f'"{font_name}"' if self.needs_quotes(font_name) else font_name font_family = self.generate_font_family(font_name) return { 'original_name': font_name, 'formatted_name': formatted_name, 'font_type': font_type, 'needs_quotes': self.needs_quotes(font_name), 'font_family': font_family, 'fallback_fonts': self.get_fallback_fonts(font_type) } # 全局字体管理器实例(线程安全) import threading _font_manager = None _font_manager_lock = threading.Lock() def get_font_manager() -> FontManager: """获取全局字体管理器实例(线程安全)""" global _font_manager if _font_manager is None: with _font_manager_lock: # 双重检查锁定模式 if _font_manager is None: _font_manager = FontManager() return _font_manager

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yuqie6/MCP-Sheet-Parser-cot'

If you have feedback or need assistance with the MCP directory API, please join our Discord server