"""
文件样式管理器
- 提供预设管理、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')