""" 文件样式管理器 - 提供预设管理、Docx<->HTML映射、验证 - 与exporter.py集成, 支持首页「文件样式设置」UI - 符合图片UI中的所有控制(字体、字号、边距、页眉页脚、方案页数等) """ import json import os from docx import Document from docx.shared import Pt, Cm from docx.enum.text import WD_ALIGN_PARAGRAPH import config DEFAULT_PRESETS = { 'standard': { 'name': '标准投标格式', 'body_font': '宋体', 'body_size_pt': 12, 'body_line_spacing': 1.5, 'heading_font': '黑体', 'heading1_size_pt': 16, 'heading2_size_pt': 14, 'margins_cm': {'top': 2.5, 'bottom': 2.5, 'left': 3.0, 'right': 2.5}, 'header_text': '标桥AI编标', 'footer_text': '第 {page} 页 / 共 {total} 页', 'page_count_target': 100, 'figure_enabled': True, 'table_enabled': True, 'description': '招标文件标准格式,宋体正文,黑体标题,标准边距' }, 'detailed': { 'name': '详细技术方案', 'body_font': '宋体', 'body_size_pt': 11, 'body_line_spacing': 1.8, 'heading_font': '黑体', 'heading1_size_pt': 18, 'heading2_size_pt': 14, 'margins_cm': {'top': 2.8, 'bottom': 2.8, 'left': 3.2, 'right': 2.8}, 'header_text': '', 'footer_text': '', 'page_count_target': 200, 'figure_enabled': True, 'table_enabled': True, 'description': '详细版,更多图表,较大页数' } } def get_preset(name='standard'): """返回预设配置""" return DEFAULT_PRESETS.get(name, DEFAULT_PRESETS['standard']) def save_preset(name, config_dict): """保存自定义预设到 data/style_presets.json""" path = os.path.join(config.DATA_DIR, 'style_presets.json') os.makedirs(config.DATA_DIR, exist_ok=True) try: if os.path.exists(path): with open(path, 'r', encoding='utf-8') as f: presets = json.load(f) else: presets = {} presets[name] = config_dict with open(path, 'w', encoding='utf-8') as f: json.dump(presets, f, ensure_ascii=False, indent=2) return True except Exception: return False def apply_preset_to_document(doc: Document, preset: dict): """将预设应用到Document,覆盖exporter.py硬编码值""" # Page setup section = doc.sections[0] m = preset.get('margins_cm', {'top': 2.5, 'bottom': 2.5, 'left': 3.0, 'right': 2.5}) section.top_margin = Cm(m['top']) section.bottom_margin = Cm(m['bottom']) section.left_margin = Cm(m['left']) section.right_margin = Cm(m['right']) # Header / Footer (basic) if preset.get('header_text'): header = section.header if header.paragraphs: p = header.paragraphs[0] p.text = preset['header_text'] p.alignment = WD_ALIGN_PARAGRAPH.CENTER if preset.get('footer_text'): footer = section.footer if footer.paragraphs: p = footer.paragraphs[0] p.text = preset['footer_text'] p.alignment = WD_ALIGN_PARAGRAPH.CENTER # The rest of the document (headings, body) is applied in exporter by reading preset return doc def docx_to_html_spec(preset): """Docx preset -> HTML/CSS for preview/dark-bid""" return { 'body': f'font-family: {preset.get("body_font", "宋体")}; font-size: {preset.get("body_size_pt", 12)}pt; line-height: {preset.get("body_line_spacing", 1.5)};', 'heading1': f'font-family: {preset.get("heading_font", "黑体")}; font-size: 16pt; font-weight: bold;', 'margins': preset.get('margins_cm', {}), 'header': preset.get('header_text', ''), 'footer': preset.get('footer_text', ''), } # For future AI extraction in parser def extract_style_hints_from_text(text: str): """Placeholder for AI to extract style requirements from tender text""" # Can be expanded with ai_client.chat using a prompt for "提取字体、页边距、图表要求" return get_preset('standard')