- 新增 PdfExporter(reportlab)与 DocxExporter(python-docx),实现与 HtmlExporter 一致的同步 render + 异步 export,v1 文本优先(标题/段落/ 行内强调与链接/列表/引用/表格/代码块/数学文本),function_plot 与 mermaid 保留源码占位并记 warning。 - service 层加 _EXPORTERS 注册表按格式分发,删除 format!=html 硬限制, 扩展名/MIME/产物清理泛化到 html/pdf/docx 三种格式。 - 新增 app/plot/renderer.py:StaticRenderRequest + StaticRenderer Protocol + FunctionPlotStaticRenderer + MermaidStaticRenderer;HtmlExporter 改经 FunctionPlotStaticRenderer 消费,去除对 render_svg 的直接依赖。 - 补齐 PDF/DOCX 魔法字节、CJK 字体、占位 warning 与 StaticRenderer 契约测试。 - 更新 Export开发说明.md。 Co-Authored-By: Claude Code <noreply@anthropic.com>
67 lines
2.3 KiB
Python
67 lines
2.3 KiB
Python
"""StaticRenderer 内部契约(契约 §10.4)。
|
|
|
|
把「静态可视化」抽象为统一请求/协议:导出器只面向 StaticRenderer,不再直接调用
|
|
``render_svg`` 等具体实现。后端当前仅能静态渲染函数图像;Mermaid 后端无渲染能力,
|
|
返回占位结果交前端渲染。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Literal, Protocol
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.plot.model import FunctionPlot, FunctionPlotParseResult, StaticRenderResult
|
|
from app.plot.parser import parse_source
|
|
from app.plot.render import render_svg
|
|
|
|
|
|
class StaticRenderRequest(BaseModel):
|
|
"""一次静态渲染请求;source_hash 供缓存/去重,theme 供主题化渲染。"""
|
|
|
|
kind: Literal["function_plot", "mermaid"]
|
|
source: str
|
|
source_hash: str = ""
|
|
theme: str | None = None
|
|
width: int | None = None
|
|
height: int | None = None
|
|
|
|
|
|
class StaticRenderer(Protocol):
|
|
"""静态渲染器协议:请求 → 渲染结果(content 为可直接内嵌的标记)。"""
|
|
|
|
def render(self, request: StaticRenderRequest) -> StaticRenderResult: ...
|
|
|
|
|
|
class FunctionPlotStaticRenderer:
|
|
"""函数图像渲染器:parse_source 解析 → render_svg 输出内嵌 SVG。
|
|
|
|
``parse`` 与 ``render_plot`` 拆开,供导出器在渲染前先拿 node_count 做文档级
|
|
累计复杂度预算、并消费解析诊断。
|
|
"""
|
|
|
|
def parse(self, request: StaticRenderRequest) -> FunctionPlotParseResult:
|
|
return parse_source(request.source)
|
|
|
|
def render(self, request: StaticRenderRequest) -> StaticRenderResult:
|
|
parsed = self.parse(request)
|
|
if parsed.plot is None:
|
|
raise ValueError("function-plot source has no valid plot")
|
|
return self.render_plot(parsed.plot)
|
|
|
|
def render_plot(self, plot: FunctionPlot) -> StaticRenderResult:
|
|
return render_svg(plot)
|
|
|
|
|
|
class MermaidStaticRenderer:
|
|
"""Mermaid 后端无渲染能力:返回空占位结果,交前端渲染。"""
|
|
|
|
def render(self, request: StaticRenderRequest) -> StaticRenderResult:
|
|
return StaticRenderResult(
|
|
content="",
|
|
mime_type="text/plain",
|
|
width=0,
|
|
height=0,
|
|
warnings=["mermaid 需前端渲染,已保留为占位代码块"],
|
|
)
|