feat(est): save print settings to ESTX
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
내역·일위대가·수식 단가산출·기계경비·기초 단가·원가계산서를 원본 테이블 구조로 편집합니다. 수량 연결, 설계변경 당초 보존, 낙찰률, 기성 회차와 사정 누계, 품목조정, 연차 배분, 소요 자원 전개, 예정공정표 화면을 제공합니다. 실행 취소·다시 실행, 검색, 행 편집, JSON 저장·열기, CSV·Excel·조달청 XML·JDL 기본 표 입출력과 브라우저 인쇄를 사용할 수 있습니다.
|
||||
|
||||
인쇄 미리보기는 화면의 100행 페이지 제한 없이 검색 결과/현재 호표의 전체 행을 표시합니다. 출력 범위에서 현재 자료의 모든 호표·행도 선택할 수 있습니다. 원본 인쇄 옵션의 41개 항목 중 CAD를 제외한 40개 서식 이름을 선택하며, ESTX에서 가져온 해당 프로젝트의 인쇄 설정을 우선 사용합니다. 내역·원가·호표/총괄·증감·동원·수량·공정은 표 자료로 출력합니다. 표지·갑지·설명서·시방서·심사 문서는 서식별 본문을 직접 입력합니다. 용지·방향·여백·글자 크기·페이지당 행·시작 쪽·업체명을 저장하고 인쇄/PDF로 출력합니다. 웹에서 변경한 인쇄 설정의 ESTX 쓰기와 원본 인쇄 도형·병합·글꼴 배치의 동등성은 지원하지 않습니다.
|
||||
인쇄 미리보기는 화면의 100행 페이지 제한 없이 검색 결과/현재 호표의 전체 행을 표시합니다. 출력 범위에서 현재 자료의 모든 호표·행도 선택할 수 있습니다. 원본 인쇄 옵션의 41개 항목 중 CAD를 제외한 40개 서식 이름을 선택하며, ESTX에서 가져온 해당 프로젝트의 인쇄 설정을 우선 사용합니다. 내역·원가·호표/총괄·증감·동원·수량·공정은 표 자료로 출력합니다. 표지·갑지·설명서·시방서·심사 문서는 서식별 본문을 직접 입력합니다. 용지·방향·여백·글자 크기·페이지당 행·시작 쪽·업체명을 저장하고 인쇄/PDF로 출력합니다. ESTX 수정 저장은 서식별 여백과 페이지당 행 수를 반영합니다. 다른 웹 인쇄 설정의 ESTX 쓰기와 원본 인쇄 도형·병합·글꼴 배치의 동등성은 지원하지 않습니다.
|
||||
|
||||
저장은 이 폴더의 `data/`에 작업을 보관하고 `.estweb.json` 파일도 내려받습니다. 브라우저 임시 저장은 같은 탭에서 복원하는 용도입니다. JSON은 모든 웹 업무와 원본 압축 파일·바이너리를 보관하는 전체 저장 형식입니다. ESTX 수정 저장은 아래 지원 조건을 따릅니다.
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ const { importEstx, unzip, Values, memoryRows } = require("./import-estx.cjs");
|
||||
const { pack } = require("./archive.cjs");
|
||||
const { updateOptions } = require("./export-options.cjs");
|
||||
const { updateQuantities } = require("./export-quantity.cjs");
|
||||
const { updatePrintOptions } = require("./export-print.cjs");
|
||||
const signature = Buffer.from("068abe8e592364cb403d71d2e3bc64d0", "hex");
|
||||
const same = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
||||
let characters;
|
||||
@@ -169,6 +170,12 @@ function exportEstx(project) {
|
||||
if (!same(project.quantitySheets || [], base.quantitySheets || []))
|
||||
project = updateQuantities(project, base, cp949, memory);
|
||||
let changed = 0;
|
||||
if (project.nativePrintChanges && Object.keys(project.nativePrintChanges).length) {
|
||||
const print = files.get("datprintoption.opt");
|
||||
if (!print) throw Error("ESTX 원본 인쇄 설정 파일 없음");
|
||||
files.set("datprintoption.opt", updatePrintOptions(print, project.nativePrintChanges));
|
||||
changed++;
|
||||
}
|
||||
if (Object.keys(optionChanges).length) {
|
||||
const options = files.get("datestoption.opt");
|
||||
if (!options) throw Error("ESTX 원본 옵션 파일 없음");
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
const { readPrintOptions } = require("./import-print.cjs");
|
||||
const sides = new Set(["PrintTop", "PrintBottom", "PrintLeft", "PrintRight"]);
|
||||
|
||||
function updatePrintOptions(buffer, changes) {
|
||||
const output = Buffer.from(buffer),
|
||||
positions = new Map();
|
||||
readPrintOptions(output, (row, fields) => {
|
||||
const id = String(row.Index);
|
||||
if (positions.has(id)) throw Error("인쇄 서식 번호 중복");
|
||||
positions.set(id, fields);
|
||||
});
|
||||
for (const [id, values] of Object.entries(changes)) {
|
||||
if (
|
||||
!/^\d+$/.test(id) ||
|
||||
!positions.has(id) ||
|
||||
!values ||
|
||||
typeof values !== "object" ||
|
||||
Array.isArray(values)
|
||||
)
|
||||
throw Error(`인쇄 서식 변경 오류: ${id}`);
|
||||
for (const [name, value] of Object.entries(values)) {
|
||||
const field = positions.get(id)[name];
|
||||
if (
|
||||
sides.has(name) &&
|
||||
field?.kind === 4 &&
|
||||
field.width === 8 &&
|
||||
typeof value === "number" &&
|
||||
Number.isFinite(value) &&
|
||||
value >= 0 &&
|
||||
value <= 50
|
||||
)
|
||||
output.writeDoubleLE(value, field.at);
|
||||
else if (
|
||||
name === "PrintLineCount" &&
|
||||
field?.kind === 1 &&
|
||||
field.width === 4 &&
|
||||
Number.isInteger(value) &&
|
||||
value >= 1 &&
|
||||
value <= 100
|
||||
)
|
||||
output.writeInt32LE(value, field.at);
|
||||
else throw Error(`인쇄 서식 ${id}.${name}: 저장할 수 없는 값`);
|
||||
}
|
||||
}
|
||||
const reread = new Map(readPrintOptions(output).map((row) => [String(row.Index), row]));
|
||||
for (const [id, values] of Object.entries(changes))
|
||||
for (const [key, value] of Object.entries(values))
|
||||
if (reread.get(id)?.[key] !== value) throw Error(`인쇄 서식 ${id}.${key} 검산 실패`);
|
||||
return output;
|
||||
}
|
||||
module.exports = { updatePrintOptions };
|
||||
@@ -1,5 +1,5 @@
|
||||
// DatPrintOption.opt memory table, as documented in ESTX_파일구조.md.
|
||||
function readPrintOptions(buffer) {
|
||||
function readPrintOptions(buffer, onRow) {
|
||||
if (buffer.length < 24 || buffer.readUInt32LE(0) !== 0xbde01996)
|
||||
throw Error("인쇄 설정 헤더 오류");
|
||||
const count = buffer.readUInt16LE(12),
|
||||
@@ -39,12 +39,15 @@ function readPrintOptions(buffer) {
|
||||
for (let r = 0; r < rows; r++) {
|
||||
take(1);
|
||||
const status = take(Math.ceil(count / 4)),
|
||||
row = {};
|
||||
row = {},
|
||||
positions = {};
|
||||
for (let i = 0; i < count; i++) {
|
||||
const { name, width, kind } = fields[i],
|
||||
state = (status[i >> 2] >> ((i % 4) * 2)) & 3;
|
||||
if (state === 1 || state === 2) continue;
|
||||
if (state !== 0) throw Error("인쇄 설정 필드 상태 오류");
|
||||
if (["PrintTop", "PrintBottom", "PrintLeft", "PrintRight", "PrintLineCount"].includes(name))
|
||||
positions[name] = { at, kind, width };
|
||||
if (kind === 1 && width === 4) row[name] = take(4).readInt32LE();
|
||||
else if (kind === 3 && width === 2) row[name] = take(2).readInt16LE();
|
||||
else if (kind === 4 && width === 8) row[name] = take(8).readDoubleLE();
|
||||
@@ -53,6 +56,7 @@ function readPrintOptions(buffer) {
|
||||
else throw Error(`인쇄 설정 필드형 오류: ${name}/${kind}/${width}`);
|
||||
}
|
||||
result.push(row);
|
||||
if (onRow) onRow(row, positions);
|
||||
}
|
||||
if (at !== buffer.length) throw Error("인쇄 설정 잔여 데이터 오류");
|
||||
return result;
|
||||
|
||||
@@ -123,7 +123,10 @@ const EstReport = (() => {
|
||||
const original = new Map(
|
||||
(EstApp.state.project.printCatalog || []).map((row) => [row.Index, row]),
|
||||
);
|
||||
catalog = defaults.map((row) => ({ ...row, ...original.get(row.Index) }));
|
||||
catalog = defaults.map((row) => ({
|
||||
...row,
|
||||
...original.get(row.Index),
|
||||
}));
|
||||
const table = EstApp.state.project.tables[EstApp.state.view];
|
||||
const s = setting(),
|
||||
{ esc } = EstApp;
|
||||
@@ -174,7 +177,11 @@ const EstReport = (() => {
|
||||
EstApp.change(() => {
|
||||
const next = { ...setting(), [key]: value };
|
||||
if (key === "form") {
|
||||
const original = catalog.find((row) => String(row.Index) === value);
|
||||
const found = catalog.find((row) => String(row.Index) === value);
|
||||
const original = found && {
|
||||
...found,
|
||||
...EstApp.state.project.nativePrintChanges?.[value],
|
||||
};
|
||||
if (original) {
|
||||
next.rowsPerPage = Math.max(
|
||||
1,
|
||||
@@ -188,6 +195,18 @@ const EstReport = (() => {
|
||||
EstApp.state.project.documents ||= {};
|
||||
EstApp.state.project.documents[next.form] = value;
|
||||
}
|
||||
if (
|
||||
["margin", "rowsPerPage"].includes(key) &&
|
||||
next.form !== "current" &&
|
||||
EstApp.state.project.printCatalog?.some((row) => String(row.Index) === next.form)
|
||||
) {
|
||||
EstApp.state.project.nativePrintChanges ||= {};
|
||||
const changes = (EstApp.state.project.nativePrintChanges[next.form] ||= {});
|
||||
if (key === "margin")
|
||||
for (const side of ["PrintTop", "PrintBottom", "PrintLeft", "PrintRight"])
|
||||
changes[side] = value;
|
||||
else changes.PrintLineCount = value;
|
||||
}
|
||||
if (key === "form") next.notes = EstApp.state.project.documents?.[value] || "";
|
||||
EstApp.state.project.printSettings = next;
|
||||
}, false);
|
||||
|
||||
Reference in New Issue
Block a user