- 미리보기 인쇄범위: 부수, 현재면, 쪽 범위(넘침·거꾸로 알림), 부수마다 간지, 일괄인쇄 - 묶음 인쇄 창 인쇄양식목록: 인쇄표·설정 이름 붙여 등록·열기·삭제, .pfot 불러오기/저장 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
169 lines
7.8 KiB
JavaScript
169 lines
7.8 KiB
JavaScript
// 원본 인쇄범위(TfrmEstPrintRange btnOK 0xcf45e4 — spec_pc §6.4 · spec_print_batch §7): 미리보기에서 부수와 범위를 골라 인쇄.
|
|
// 현재면 = 미리보기에 보이는 쪽 한 장 × 부수(간지 없음) · 현재항목 전체 = 쪽 범위(0 이면 1~전체) × 부수, 부수마다 간지(그 서식 간지
|
|
// 인쇄가 켜져 있으면) · 일괄인쇄 = 묶음 인쇄. 그리고 인쇄양식목록(TfrmEstPrint2 btnFormatList 0xcf9108 → TfrmFormatFileList):
|
|
// 지금 인쇄표(서식 고르기·간지·용지 설정)를 이름 붙여 보관함(종류 printlist)에 등록하고, 열면 인쇄표를 통째로 바꾼다.
|
|
const EstPrintRange = (() => {
|
|
const esc = (v) =>
|
|
String(v ?? "").replace(
|
|
/[&<>"]/g,
|
|
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
|
|
);
|
|
const pagesOf = (doc) => [...doc.querySelectorAll("section.page")];
|
|
|
|
// 범위 고르기: 쪽 목록(0부터) 또는 오류
|
|
function pick(total, { mode, current, from, to }) {
|
|
if (mode === "current") return [Math.min(Math.max(current, 0), total - 1)];
|
|
let a = Number(from) || 0,
|
|
b = Number(to) || 0;
|
|
if (a === 0) [a, b] = [1, total];
|
|
if (b > total) throw Error("인쇄범위가 전체쪽수를 넘었습니다.");
|
|
if (a > b) throw Error("시작페이지가 끝페이지보다 큽니다.");
|
|
return Array.from({ length: b - a + 1 }, (_, i) => a - 1 + i);
|
|
}
|
|
|
|
function panel() {
|
|
const frame = EstApp.byId("print-frame"),
|
|
doc = frame?.contentDocument;
|
|
if (!doc?.body) throw Error("인쇄 미리보기가 준비되지 않았습니다.");
|
|
const total = Math.max(1, pagesOf(doc).length);
|
|
let box = EstApp.byId("print-range-box");
|
|
if (box) return box.remove();
|
|
box = document.createElement("div");
|
|
box.id = "print-range-box";
|
|
box.className = "business-tools";
|
|
box.innerHTML = `<b>인쇄범위</b> 전체쪽수 ${total}쪽 <label>부수 <input type="number" min="1" value="1" data-pr="copies" style="width:50px"></label>
|
|
<label><input type="radio" name="pr-mode" value="current"> 현재면 인쇄</label>
|
|
<label><input type="radio" name="pr-mode" value="range" checked> 현재항목 전체인쇄</label>
|
|
<label>쪽 <input type="number" min="0" value="0" data-pr="from" style="width:55px"> ~ <input type="number" min="0" value="0" data-pr="to" style="width:55px"></label>
|
|
<label><input type="radio" name="pr-mode" value="batch"> 일괄인쇄</label> <button data-action="print-range-ok">인 쇄</button>`;
|
|
frame.before(box);
|
|
}
|
|
|
|
function run(state) {
|
|
const box = EstApp.byId("print-range-box"),
|
|
mode = box.querySelector('[name="pr-mode"]:checked').value;
|
|
if (mode === "batch") return EstPrintBatch.handle("print-batch", state);
|
|
const frame = EstApp.byId("print-frame"),
|
|
doc = frame.contentDocument,
|
|
pages = pagesOf(doc),
|
|
copies = Math.max(1, Number(box.querySelector('[data-pr="copies"]').value) || 1);
|
|
// 현재면: 미리보기 창에 지금 보이는 쪽
|
|
const y = frame.contentWindow.scrollY;
|
|
const current = Math.max(
|
|
0,
|
|
pages.findLastIndex((p) => p.offsetTop <= y + 10),
|
|
);
|
|
const list = pages.length
|
|
? pick(pages.length, {
|
|
mode,
|
|
current,
|
|
from: box.querySelector('[data-pr="from"]').value,
|
|
to: box.querySelector('[data-pr="to"]').value,
|
|
})
|
|
: [0];
|
|
const settings = state.project.printSettings || {},
|
|
form = (state.project.printCatalog || []).find(
|
|
(r) => String(r.Index) === String(settings.form),
|
|
),
|
|
gangi = mode === "range" && Number(form?.PrintGangiShow) === 1;
|
|
const body = pages.length ? list.map((i) => pages[i].outerHTML).join("") : doc.body.innerHTML;
|
|
const one =
|
|
(gangi
|
|
? `<section class="page" style="height:170mm;display:flex;align-items:center;justify-content:center;font-size:30pt;font-weight:bold">${esc(form.Name2 || form.Name)}</section>`
|
|
: "") + body;
|
|
const out = doc.documentElement.cloneNode(true);
|
|
out.querySelector("body").innerHTML = Array.from({ length: copies }, () => one).join("");
|
|
let target = EstApp.byId("print-range-frame");
|
|
if (!target) {
|
|
target = document.createElement("iframe");
|
|
target.id = "print-range-frame";
|
|
target.style.display = "none";
|
|
document.body.append(target);
|
|
}
|
|
target.onload = () => target.contentWindow.print();
|
|
target.srcdoc = "<!doctype html>" + out.outerHTML;
|
|
return { pages: list.length, copies };
|
|
}
|
|
|
|
// 인쇄양식목록
|
|
const KIND = "printlist";
|
|
const post = (body) => ({
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body),
|
|
});
|
|
let forms = [];
|
|
async function list() {
|
|
forms = (await EstApp.api("/api/forms")).filter((f) => f.kind === KIND);
|
|
EstApp.showDialog(
|
|
"인쇄양식목록",
|
|
`<button data-action="pfl-register">현재 인쇄양식 등록</button> <button data-action="pfot-load">파일에서 불러오기(.pfot)</button> <button data-action="pfot-save">파일로 저장(.pfot)</button> <button data-action="print-batch">인쇄 창으로</button><table class="sheet" style="margin-top:8px"><thead><tr><th>양식 이름</th><th>등록</th><th>작업</th></tr></thead><tbody>${
|
|
forms
|
|
.map(
|
|
(f) =>
|
|
`<tr><td>${esc(f.name)}</td><td>${esc(String(f.saved || "").slice(0, 10))}</td><td><button data-action="pfl-open" data-name="${esc(f.name)}">열 기</button><button data-action="pfl-delete" data-name="${esc(f.name)}">삭 제</button></td></tr>`,
|
|
)
|
|
.join("") || '<tr><td colspan="3">등록된 인쇄양식이 없습니다.</td></tr>'
|
|
}</tbody></table>`,
|
|
);
|
|
}
|
|
const snapshot = (project) =>
|
|
structuredClone({
|
|
printCatalog: project.printCatalog,
|
|
printFormat: project.printFormat,
|
|
printSettings: project.printSettings,
|
|
printBatch: project.printBatch,
|
|
});
|
|
function apply(project, data) {
|
|
for (const k of ["printCatalog", "printFormat", "printSettings", "printBatch"])
|
|
if (data[k] === undefined) delete project[k];
|
|
else project[k] = structuredClone(data[k]);
|
|
}
|
|
async function run2(name, state, node) {
|
|
if (name === "print-format-list") return list();
|
|
if (name === "pfl-register") {
|
|
const title = prompt("인쇄양식 이름", state.project.name || "");
|
|
if (!title?.trim()) return;
|
|
if (
|
|
forms.some((f) => f.name === title.trim()) &&
|
|
!confirm("같은 이름이 있습니다. 덮어 쓰시겠습니까?")
|
|
)
|
|
return;
|
|
await EstApp.api(
|
|
"/api/forms",
|
|
post({ kind: KIND, name: title.trim(), data: snapshot(state.project) }),
|
|
);
|
|
return list();
|
|
}
|
|
const form = forms.find((f) => f.name === node.dataset.name);
|
|
if (!form) throw Error("양식을 찾을 수 없습니다. 목록을 다시 여세요.");
|
|
if (name === "pfl-open") {
|
|
EstApp.change((project) => apply(project, form.data), false);
|
|
state.notice = `인쇄양식 「${form.name}」을 열었습니다.`;
|
|
return EstPrintBatch.handle("print-batch", state);
|
|
}
|
|
if (!confirm(`「${form.name}」 인쇄양식을 지우시겠습니까?`)) return;
|
|
await EstApp.api("/api/forms/delete", post({ kind: KIND, name: form.name }));
|
|
return list();
|
|
}
|
|
|
|
function handle(name, state, node) {
|
|
try {
|
|
if (name === "print-range") panel();
|
|
else if (name === "print-range-ok") run(state);
|
|
else if (["print-format-list", "pfl-register", "pfl-open", "pfl-delete"].includes(name))
|
|
run2(name, state, node).catch((error) => {
|
|
state.notice = error.message;
|
|
EstApp.render();
|
|
});
|
|
else return false;
|
|
} catch (error) {
|
|
const note = EstApp.byId("print-error");
|
|
if (note) note.textContent = error.message;
|
|
else throw error;
|
|
}
|
|
return true;
|
|
}
|
|
return { pick, snapshot, apply, handle };
|
|
})();
|