- 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>
89 lines
4.2 KiB
TypeScript
89 lines
4.2 KiB
TypeScript
/* =============================================================================
|
|
* common_util_spreadsheet_node.ts
|
|
* 산출근거 스프레드시트 풀이를 **서버가** 돌리는 진입점 — [저장] 때 정본 `계산값`.
|
|
*
|
|
* 풀이는 화면이 쓰는 `A00_Common/spreadsheet/spreadsheet_recalc.ts` 그대로 — 여기에는 계산이 없음(CLAUDE.md 5장 ②).
|
|
* 파이썬 껍데기 = `common_util_spreadsheet.py` · 번들 = `npm run build:spreadsheet`.
|
|
*
|
|
* 실행: node <번들> <입력.json> <출력.json>
|
|
* 입력 { books?: Workbook[], tables?: TableAsk[] }
|
|
* 출력 { results: ({ ok: true, 계산값 } | { ok: false, 까닭 })[],
|
|
* tables: ({ ok: true, 표, 시트, 범위, 값 } | { ok: false, 까닭 })[] } — 입력 차례 그대로 · 하나가 죽어도 나머지는 풂
|
|
* 표(41-1) = 면적산출 시트 · 범위 → CAD 표 객체(TableEntity 꼴) — 풀이는 `A00_Common/spreadsheet/spreadsheet_area_table.ts`
|
|
* (화면 도면 패널의 저장 전 실시간 표 값과 같은 코드 · 112-1) · 저장 안 함.
|
|
* 인쇄(534-2 · 요구 347-5) = 입력 `prints: {book, figures?}[]` → 출력 `prints: ({ ok: true, html } | { ok: false, 까닭 })[]` —
|
|
* `A00_Common/spreadsheet/spreadsheet_print_html.ts` 한 벌 · figures = 그림 열쇠 → data: 주소 · PDF 는 파이썬이 만듦.
|
|
* 끝 코드: 0 성공 / 2 인자 오류
|
|
* ⚠ `A00_Common/spreadsheet/` 밖에 둠 — node:fs 가 브라우저 번들로 새지 않게.
|
|
* ========================================================================== */
|
|
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { parseRange } from "../A00_Common/spreadsheet/spreadsheet_address";
|
|
import { areaTable, type TableAsk } from "../A00_Common/spreadsheet/spreadsheet_area_table";
|
|
import { printHtml } from "../A00_Common/spreadsheet/spreadsheet_print_html";
|
|
import { recalcWorkbook } from "../A00_Common/spreadsheet/spreadsheet_recalc";
|
|
import type { Workbook } from "../A00_Common/spreadsheet/spreadsheet_types";
|
|
|
|
/** 면적 시트에 보낸 범위 이름을 더하거나(같은 이름 있으면) 범위를 갱신(104-1 · 도면 [저장] 때 새 연결 줄마다) */
|
|
interface NameAsk {
|
|
book: Workbook;
|
|
시트: string;
|
|
범위: string;
|
|
이름: string;
|
|
}
|
|
|
|
/** 이름정의를 더하거나(있으면) 범위만 바꿈 — 책 사본을 돌려줌(저장은 python 쪽) */
|
|
function defineAreaName(ask: NameAsk): { book: Workbook } {
|
|
const book = ask.book;
|
|
const sheet =
|
|
book.시트.find((s) => s.id === ask.시트) ?? book.시트.find((s) => s.이름 === ask.시트);
|
|
if (!sheet) throw new Error(`없는 시트 「${ask.시트}」`);
|
|
const range = parseRange(ask.범위);
|
|
if (!range) throw new Error(`범위 꼴 아님 「${ask.범위}」`);
|
|
const kept = (sheet.이름정의 ?? []).filter((n) => n.이름 !== ask.이름);
|
|
sheet.이름정의 = [...kept, { 이름: ask.이름, 범위: range }];
|
|
return { book };
|
|
}
|
|
|
|
const [inputPath, outputPath] = process.argv.slice(2);
|
|
if (!inputPath || !outputPath) {
|
|
console.error("사용법: node <번들> <입력.json> <출력.json>");
|
|
process.exit(2);
|
|
}
|
|
|
|
const input = JSON.parse(readFileSync(inputPath, "utf8")) as {
|
|
books?: Workbook[];
|
|
tables?: TableAsk[];
|
|
names?: NameAsk[];
|
|
prints?: { book: Workbook; figures?: Record<string, string> }[];
|
|
};
|
|
const results = (input.books ?? []).map((book) => {
|
|
try {
|
|
return { ok: true, 계산값: recalcWorkbook(book) };
|
|
} catch (error) {
|
|
return { ok: false, 까닭: error instanceof Error ? error.message : String(error) };
|
|
}
|
|
});
|
|
const tables = (input.tables ?? []).map((ask) => {
|
|
try {
|
|
return { ok: true, ...areaTable(ask) };
|
|
} catch (error) {
|
|
return { ok: false, 까닭: error instanceof Error ? error.message : String(error) };
|
|
}
|
|
});
|
|
const names = (input.names ?? []).map((ask) => {
|
|
try {
|
|
return { ok: true, ...defineAreaName(ask) };
|
|
} catch (error) {
|
|
return { ok: false, 까닭: error instanceof Error ? error.message : String(error) };
|
|
}
|
|
});
|
|
const prints = (input.prints ?? []).map((ask) => {
|
|
try {
|
|
return { ok: true, html: printHtml(ask.book, ask.figures) };
|
|
} catch (error) {
|
|
return { ok: false, 까닭: error instanceof Error ? error.message : String(error) };
|
|
}
|
|
});
|
|
writeFileSync(outputPath, JSON.stringify({ results, tables, names, prints }));
|