feat(534-2): 내장 엑셀 내보내기 — 인쇄 HTML(Node print) · xlsx · SVG→PNG · PDF

- spreadsheet_print_html: 통합문서 → 인쇄 HTML(DOM 없음 · 화면 엔진 값 · formatValue · 병합 · colgroup 폭 · 쪽 나눔 덩이 · thead 머리행 반복 · 그림 img · 시트마다 @page)
- 서식 → CSS 를 클립보드에서 떼어 공용(medium · thick · double 굵기 반영)
- 서버 Node 진입 prints · 파이썬 print_htmls · workbook_pdf(공용 PDF 엔진 page_size=None)
- common_util_workbook_xlsx: openpyxl 로 값 · 식 · 서식 · 병합 · 폭 · 높이 · 틀 고정 · 인쇄 설정 · 쪽 나눔 · 그림(두 칸 닻)
- common_util_svg_png: Playwright 로 SVG → PNG(PyMuPDF 안 씀)
- 시험 test_spreadsheet_print(인쇄 HTML · xlsx 되읽기 · PNG 크기 · PDF 쪽 수)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 19:33:57 +09:00
co-authored by Claude Opus 5.5
parent 280072a16d
commit f20aea412a
7 changed files with 729 additions and 50 deletions
+37
View File
@@ -9,6 +9,7 @@ CLAUDE.md 5장 ② — 계산은 `A00_Common/spreadsheet/` 한 벌. 여기는
from __future__ import annotations
import base64
import copy
from typing import Any
@@ -77,3 +78,39 @@ def define_names(asks: list[dict[str, Any]]) -> list[dict[str, Any]]:
if out is None:
raise RecalcError("스프레드시트 계산 번들 실행 실패")
return out["names"]
def print_htmls(asks: list[dict[str, Any]]) -> list[str]:
"""통합문서 → 인쇄 HTML(534-2 · 번들 `printHtml` 한 벌) —
ask = `{book, figures?: {열쇠: PNG 바이트}}`.
물음 차례 그대로 HTML. 하나라도 못 만들면 `RecalcError`."""
if _stale() and not build_bundle(NPM_SCRIPT):
raise RecalcError("스프레드시트 계산 번들을 못 만듦")
prints = [
{
"book": ask["book"],
"figures": {
key: "data:image/png;base64," + base64.b64encode(png).decode()
for key, png in (ask.get("figures") or {}).items()
},
}
for ask in asks
]
out = run_bundle_json(BUNDLE, NPM_SCRIPT, {"prints": prints})
if out is None:
raise RecalcError("스프레드시트 계산 번들 실행 실패")
htmls: list[str] = []
for result in out["prints"]:
if not result.get("ok"):
raise RecalcError(f"인쇄 HTML 실패 — {result.get('까닭')}")
htmls.append(result["html"])
return htmls
def workbook_pdf(book: dict[str, Any], figures: dict[str, bytes] | None = None) -> bytes:
"""통합문서 → PDF(요구 347-5) — 인쇄 HTML → 공용 PDF 엔진(쪽 크기 · 여백 = 시트마다 CSS @page).
실패 = `RecalcError` 또는 `common_util_print_pdf.PrintError`."""
from common_util.common_util_print_pdf import render_html_to_pdf
html = print_htmls([{"book": book, "figures": figures}])[0]
return render_html_to_pdf(html, page_size=None)