From b98fbccdd0180088be7018ae397369f4c5fa9f25 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 28 Sep 2026 13:30:50 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix(spreadsheet):=20=ED=81=AC=EA=B8=B0=20?= =?UTF-8?q?=EC=98=88=EC=82=B0=20=ED=95=A9=20128=20KB=20=E2=80=94=20?= =?UTF-8?q?=EC=97=94=EC=A7=84=2048=20=C2=B7=20=ED=99=94=EB=A9=B4=2072=20?= =?UTF-8?q?=C2=B7=20css=208=20KB(=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A7=80?= =?UTF-8?q?=EC=8B=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 계약 머리 주석 · 크기 시험 숫자 같이 올림 · 지금 크기 엔진 31.5 · 화면 28.8 · css 2.9 KB(합 63.2 KB) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019Kyw9qnkJ94pC1A4PfpDtA --- A00_Common/spreadsheet/spreadsheet_types.ts | 4 ++-- resources/tester/spreadsheet/test_spreadsheet_size.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/A00_Common/spreadsheet/spreadsheet_types.ts b/A00_Common/spreadsheet/spreadsheet_types.ts index 97c87688..b941b2a2 100644 --- a/A00_Common/spreadsheet/spreadsheet_types.ts +++ b/A00_Common/spreadsheet/spreadsheet_types.ts @@ -12,8 +12,8 @@ * · 보이는 칸만 그림 · 파일마다 700 줄 안쪽(예산 넘기 전에 쪼갬) · 화면 글자는 그 파일 안에 둠. * · xlsx 가져오기 · 내보내기 · 인쇄 · PDF 는 만들지 않음(단추도 없음). xlsx 는 시험(F)만 읽음. * - * ── 크기 예산 (vite build · 압축(gzip) 뒤 · M02 산출근거를 열 때만 받는 조각) ────── - * 엔진 A+B 32 KB · 화면 C+D+E 28 KB · css 4 KB · 합 64 KB 안쪽. 재는 곳 = F(빌드 조각 크기 시험). + * ── 크기 예산 (vite build · 압축(gzip) 뒤 · M02 면적산출 · 재료산출을 열 때만 받는 조각) ── + * 엔진 A+B 48 KB · 화면 C+D+E 72 KB · css 8 KB · 합 128 KB 안쪽. 재는 곳 = F(빌드 조각 크기 시험). * * ── 파일 · 주인 · 예산 줄 수 ───────────────────────────────────────────────── * 0 spreadsheet_types.ts 이 계약(타입) ~450 diff --git a/resources/tester/spreadsheet/test_spreadsheet_size.py b/resources/tester/spreadsheet/test_spreadsheet_size.py index 926a202d..549afd08 100644 --- a/resources/tester/spreadsheet/test_spreadsheet_size.py +++ b/resources/tester/spreadsheet/test_spreadsheet_size.py @@ -1,6 +1,6 @@ """F — 스프레드시트 크기 예산 · M02 만 늦게 불러옴(계약 `spreadsheet_types.ts` 머리). -예산(압축 gzip 뒤) — 엔진 A+B 32 KB · 화면 C+D+E 28 KB · css 4 KB · 합 64 KB. +예산(압축 gzip 뒤) — 엔진 A+B 48 KB · 화면 C+D+E 72 KB · css 8 KB · 합 128 KB. 엔진 = 엔진 파일만 따로 묶은 크기 · 합 = 실제 앱 빌드에서 스프레드시트를 열 때 받는 조각 (공용 조각까지 넉넉히 셈) · 화면 = 합 − 엔진. """ @@ -16,7 +16,7 @@ import pytest ROOT = Path(__file__).resolve().parents[3] SCRIPT = Path(__file__).with_name("spreadsheet_size.mjs") KB = 1024 -BUDGET = {"engine": 32 * KB, "view": 28 * KB, "css": 4 * KB, "total": 64 * KB} +BUDGET = {"engine": 48 * KB, "view": 72 * KB, "css": 8 * KB, "total": 128 * KB} pytestmark = pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음") From e0f9df9ba922d42965256df77a88e288ae08438d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 28 Sep 2026 13:35:18 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix(spreadsheet):=20=EC=97=91=EC=85=80=20?= =?UTF-8?q?=ED=9A=8C=EA=B3=84=20=EC=88=AB=EC=9E=90=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EB=81=9D=20=EC=97=AD=EC=8A=AC=EB=9E=98=EC=8B=9C=20=EB=82=A8?= =?UTF-8?q?=EB=8D=98=20=EA=B2=83=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - mso-number-format 처럼 값 안에 이스케이프된 `\;`(구역 나눔 자리표시, 회계·통화 형식처럼 구역이 여럿인 형식에 흔함)를 선언 나눔 `parseStyleAttr` 가 진짜 `;` 로 착각해 값을 그 자리서 잘라 버리던 것 고침 — `\` 뒤 글자는 통째로 건너뛰고 이스케이프 안 된 `;` 에서만 나누는 `splitDecls` 로 바꿈 - 잘리면 값 끝에 역슬래시 하나만 남고 뒤 구역이 통째로 날아가던 것(사용자 저장본 material/rv.json 서식 형식 끝 '\' 로 나타남)이었음 — 고친 뒤 4구역 회계 형식 (양수·음수·0·글자)이 통째로 살아 들어옴 - resources/tester/spreadsheet/fixture_excel_accounting_fmt.html 새 견본으로 ORCA 실 엔진 확인 — 형식이 `_-* #,##0.00_-;-* #,##0.00_-;_-* "-"??_-;_-@_-` 로 온전히 들어옴(역슬래시 없음), 기존 fixture_excel_style_paste.html 회귀 없음 확인 - 수식 흔적(x:fmla) 확인 — 기존 견본엔 x:str/x:num 만 있고 x:fmla 없음(수식 칸도 x:str 이 `=...` 글 그대로 담아 기존 「=」 앞머리 잡기로 이미 됨). 사용자 실제 클립보드 견본(main_desktop_1)은 아직 안 옴 — 오면 그걸로 다시 봄 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AeeQRLknEpP4SFjVkjDWDs --- .../spreadsheet/spreadsheet_clipboard.ts | 9 ++++++- .../fixture_excel_accounting_fmt.html | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 resources/tester/spreadsheet/fixture_excel_accounting_fmt.html diff --git a/A00_Common/spreadsheet/spreadsheet_clipboard.ts b/A00_Common/spreadsheet/spreadsheet_clipboard.ts index 2c03858b..06c25067 100644 --- a/A00_Common/spreadsheet/spreadsheet_clipboard.ts +++ b/A00_Common/spreadsheet/spreadsheet_clipboard.ts @@ -219,10 +219,17 @@ function walkTable(table: HTMLTableElement): TableWalk { return { rows: rows.length, cols, owner, merges }; } +/** 선언 나눔 — `mso-number-format:"...\;..."` 안 이스케이프된 `\;`(구역 나눔 자리표시)는 진짜 + * 선언 끝이 아님(엑셀 회계 형식처럼 구역이 여럿인 값을 여기서 잘라 버리면 값 끝에 떨어진 `\` 만 + * 남아 형식이 깨짐) — `\` 뒤 글자는 통째로 건너뛰어 그 안 `;` 는 안 자름. */ +function splitDecls(s: string): string[] { + return s.split(/(? { const out: Record = {}; - for (const decl of styleAttr.split(";")) { + for (const decl of splitDecls(styleAttr)) { const i = decl.indexOf(":"); if (i < 0) continue; const key = decl.slice(0, i).trim().toLowerCase(); diff --git a/resources/tester/spreadsheet/fixture_excel_accounting_fmt.html b/resources/tester/spreadsheet/fixture_excel_accounting_fmt.html new file mode 100644 index 00000000..a186afc3 --- /dev/null +++ b/resources/tester/spreadsheet/fixture_excel_accounting_fmt.html @@ -0,0 +1,24 @@ + + + + + + + + + + +
1234.5
+ + From 137796158bd8e62781d648e44f0c76b2a7ba3bb4 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 28 Sep 2026 13:37:41 +0900 Subject: [PATCH 3/6] =?UTF-8?q?chore:=20=EB=82=A8=EC=9D=80=20=EC=97=91?= =?UTF-8?q?=EC=85=80=20=EC=9E=A0=EA=B8=88=20=ED=8C=8C=EC=9D=BC=202?= =?UTF-8?q?=EA=B0=9C=20=EC=A0=80=EC=9E=A5=EC=86=8C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=BA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_017cTe8U3dnigRfEmiscHHpp --- .../수량산출/~$4.2 수량산출(2공구).xlsx | Bin 165 -> 0 bytes .../수량산출/~$4.3 수량산출(3공구).xlsx | Bin 165 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 resources/knowledge/original/실무문서/2024년 간선임도(기번3-울진.대흥)(2공구)/수량산출/~$4.2 수량산출(2공구).xlsx delete mode 100644 resources/knowledge/original/실무문서/2024년 간선임도(기번3-울진.대흥)(3공구)/수량산출/~$4.3 수량산출(3공구).xlsx diff --git a/resources/knowledge/original/실무문서/2024년 간선임도(기번3-울진.대흥)(2공구)/수량산출/~$4.2 수량산출(2공구).xlsx b/resources/knowledge/original/실무문서/2024년 간선임도(기번3-울진.대흥)(2공구)/수량산출/~$4.2 수량산출(2공구).xlsx deleted file mode 100644 index 6b57c04deaa023b648c1e51e7088d6d41e670401..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 ecmZSE_igv*t=kny1k4OajvhR4VzUARJs<#H78Sh! diff --git a/resources/knowledge/original/실무문서/2024년 간선임도(기번3-울진.대흥)(3공구)/수량산출/~$4.3 수량산출(3공구).xlsx b/resources/knowledge/original/실무문서/2024년 간선임도(기번3-울진.대흥)(3공구)/수량산출/~$4.3 수량산출(3공구).xlsx deleted file mode 100644 index 6b57c04deaa023b648c1e51e7088d6d41e670401..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 ecmZSE_igv*t=kny1k4OajvhR4VzUARJs<#H78Sh! From 41a717f62b192b8f15ce9d32f06553b305de5fff Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 28 Sep 2026 13:39:50 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix(spreadsheet):=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EB=81=9D=C2=B7=EA=B5=AC=EC=97=AD=20=EB=82=98=EB=88=94=20?= =?UTF-8?q?=EC=95=9E=20=ED=99=80=EB=A1=9C=20=EB=82=A8=EC=9D=80=20=EC=97=AD?= =?UTF-8?q?=EC=8A=AC=EB=9E=98=EC=8B=9C=EA=B0=80=20=E2=82=A9=20=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=B4=EC=9D=B4=EB=8D=98=20=EA=B2=83=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 브레인이 증거로 찾은 rv.json(사용자 붙여넣기 저장분)의 형식 문자열이 끝에 역슬래시 하나만 남아(`_-* #,##0.00_-\` 등) 있었음 — 엑셀 mso-number-format 의 `\;`(구역 나눔 이스케이프) 자르기가 깨진 자투리. numfmt 가 이 홀로 남은 역슬래시를 글자로 찍었고 굴림 등 한글 글꼴은 역슬래시(0x5C)를 ₩ 모양으로 그려 사용자 화면에 ₩ 가 붙어 보였음. 세 자리(splitSections · tokenize · detectDateFormat)에서 ① 형식 끝에 다음 글자가 없는 역슬래시 ② 구역 나눔 `;` 바로 앞 역슬래시를 모두 버림(뒤 `;` 는 그대로 나눔으로 처리)으로 고침. 이미 저장된 rv.json 의 13 곳도 자투리 역슬래시를 지움. 사용자 견본값 2.20 · 1.70 · -6.82 로 회귀 시험 세 건 더함 — 시험 196건 전부 통과. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RrQ65VtGZbae2VKgYVMhmc --- A00_Common/spreadsheet/spreadsheet_numfmt.ts | 14 ++++++---- resources/master_template/material/rv.json | 26 +++++++++---------- .../spreadsheet/spreadsheet_b_test_entry.ts | 17 ++++++++++++ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/A00_Common/spreadsheet/spreadsheet_numfmt.ts b/A00_Common/spreadsheet/spreadsheet_numfmt.ts index 66215037..506bd21b 100644 --- a/A00_Common/spreadsheet/spreadsheet_numfmt.ts +++ b/A00_Common/spreadsheet/spreadsheet_numfmt.ts @@ -44,7 +44,10 @@ function splitSections(code: string): string[] { cur += ch; continue; } - if (ch === "\\" && i + 1 < code.length) { + if (ch === "\\") { + // 형식 끝에 홀로 남은 역슬래시 · 구역 나눔(`;`) 바로 앞 역슬래시는 깨진 자투리로 보고 버림 + // (엑셀 붙여넣기가 잘려 남긴 것 — 2026-09-28 실측 rv.json). 그 밖엔 원래대로 다음 글자를 이스케이프. + if (i + 1 >= code.length || code[i + 1] === ";") continue; cur += ch + code[i + 1]; i++; continue; @@ -108,8 +111,9 @@ function tokenize(s: string): { toks: Tok[]; fillChar?: string } { i = j; continue; } - if (ch === "\\" && i + 1 < s.length) { - toks.push({ k: "lit", ch: s[i + 1] }); + if (ch === "\\") { + // 홀로 남은 역슬래시(뒤에 이스케이프할 글자가 없음)는 굴림 등 글꼴에서 ₩ 모양으로 보여 버림. + if (i + 1 < s.length) toks.push({ k: "lit", ch: s[i + 1] }); i++; continue; } @@ -449,8 +453,8 @@ function detectDateFormat(s: string): DatePart[] | null { i = j + 1; continue; } - if (ch === "\\" && i + 1 < s.length) { - parts.push({ k: "lit", text: s[i + 1] }); + if (ch === "\\") { + if (i + 1 < s.length) parts.push({ k: "lit", text: s[i + 1] }); i += 2; continue; } diff --git a/resources/master_template/material/rv.json b/resources/master_template/material/rv.json index 3f3a6ad6..2709b17b 100644 --- a/resources/master_template/material/rv.json +++ b/resources/master_template/material/rv.json @@ -52,7 +52,7 @@ }, { "가로": "center", - "형식": "_ * #,##0.00_ \\", + "형식": "_ * #,##0.00_ ", "테두리": { "위": { "선": "hair" @@ -61,7 +61,7 @@ }, { "가로": "center", - "형식": "_-* #,##0.00_-\\", + "형식": "_-* #,##0.00_-", "테두리": { "위": { "선": "hair" @@ -86,7 +86,7 @@ }, { "가로": "center", - "형식": "0.00_)\\", + "형식": "0.00_)", "테두리": { "위": { "선": "hair" @@ -132,7 +132,7 @@ }, { "가로": "center", - "형식": "_-* #,##0_-\\", + "형식": "_-* #,##0_-", "테두리": { "위": { "선": "hair" @@ -144,7 +144,7 @@ }, { "가로": "center", - "형식": "_-* #,##0.0_-\\", + "형식": "_-* #,##0.0_-", "테두리": { "위": { "선": "hair" @@ -168,7 +168,7 @@ }, { "가로": "center", - "형식": "_-* #,##0.00_-\\", + "형식": "_-* #,##0.00_-", "테두리": { "위": { "선": "hair" @@ -179,7 +179,7 @@ } }, { - "형식": "_-* #,##0.00_-\\", + "형식": "_-* #,##0.00_-", "테두리": { "위": { "선": "hair" @@ -224,7 +224,7 @@ }, { "가로": "center", - "형식": "0.00_)\\", + "형식": "0.00_)", "테두리": { "위": { "선": "hair" @@ -282,7 +282,7 @@ }, { "가로": "center", - "형식": "_ * #,##0.00_ \\", + "형식": "_ * #,##0.00_ ", "테두리": { "아래": { "선": "hair" @@ -298,7 +298,7 @@ }, { "가로": "center", - "형식": "0.00_)\\", + "형식": "0.00_)", "테두리": { "아래": { "선": "hair" @@ -414,11 +414,11 @@ }, { "가로": "center", - "형식": "_ * #,##0.00_ \\" + "형식": "_ * #,##0.00_ " }, { "가로": "center", - "형식": "_-* #,##0.00_-\\" + "형식": "_-* #,##0.00_-" }, { "테두리": { @@ -495,7 +495,7 @@ }, { "가로": "center", - "형식": "0.00_)\\", + "형식": "0.00_)", "테두리": { "아래": { "선": "thin" diff --git a/resources/tester/spreadsheet/spreadsheet_b_test_entry.ts b/resources/tester/spreadsheet/spreadsheet_b_test_entry.ts index cbdc8ca3..004fe333 100644 --- a/resources/tester/spreadsheet/spreadsheet_b_test_entry.ts +++ b/resources/tester/spreadsheet/spreadsheet_b_test_entry.ts @@ -545,6 +545,23 @@ ok( fmt(frac(1234567n), "#,##0") === "1,234,567", fmt(frac(1234567n), "#,##0"), ); +ok( + "형식 끝 홀로 남은 역슬래시는 ₩ 로 안 보임(rv.json 실측)", + !fmt(toFrac("2.20"), "_-* #,##0.00_-\\").includes("₩") && + fmt(toFrac("2.20"), "_-* #,##0.00_-\\") === " 2.20 ", + fmt(toFrac("2.20"), "_-* #,##0.00_-\\"), +); +ok( + "형식 끝 홀로 남은 역슬래시(0.00_) 꼴)도 ₩ 로 안 보임", + !fmt(toFrac("1.70"), "0.00_)\\").includes("₩") && fmt(toFrac("1.70"), "0.00_)\\") === "1.70 ", + fmt(toFrac("1.70"), "0.00_)\\"), +); +ok( + "구역 나눔 앞 역슬래시도 ₩ 로 안 남고 나눔이 그대로 일어남", + !fmt(toFrac("-6.82"), "0.00_-\\;-0.00_-").includes("₩") && + fmt(toFrac("-6.82"), "0.00_-\\;-0.00_-") === "-6.82 ", + fmt(toFrac("-6.82"), "0.00_-\\;-0.00_-"), +); // ── 채우기 ────────────────────────────────────────────────────────────── From 8739027872cba7b9a26314454e76171fea9ccd72 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 28 Sep 2026 13:39:56 +0900 Subject: [PATCH 5/6] =?UTF-8?q?test(spreadsheet):=20=EC=97=91=EC=85=80=20?= =?UTF-8?q?=EC=B9=B8=20=EB=B2=94=EC=9C=84=20=EB=B3=B5=EC=82=AC=20=EC=9B=90?= =?UTF-8?q?=EB=B3=B8=20fixture(HTML=C2=B7=EA=B8=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_017cTe8U3dnigRfEmiscHHpp --- .../spreadsheet/fixture_excel_user_won.html | 745 ++++++++++++++++++ .../spreadsheet/fixture_excel_user_won.txt | 6 + 2 files changed, 751 insertions(+) create mode 100644 resources/tester/spreadsheet/fixture_excel_user_won.html create mode 100644 resources/tester/spreadsheet/fixture_excel_user_won.txt diff --git a/resources/tester/spreadsheet/fixture_excel_user_won.html b/resources/tester/spreadsheet/fixture_excel_user_won.html new file mode 100644 index 00000000..8f4cbe79 --- /dev/null +++ b/resources/tester/spreadsheet/fixture_excel_user_won.html @@ -0,0 +1,745 @@ +Version:1.0 +StartHTML:0000000366 +EndHTML:0000024378 +StartFragment:0000015411 +EndFragment:0000024326 +SourceURL:file:///D:/02_Software_Prog/임도설계%20및%20견적자동화%20프로그램%20개발/resources/knowledge/original/실무문서/2024년%20간선임도(기번3-울진.대흥)(공통)/1.%20설계원본/02%20산출자료/5.%20구조도(기번3).xlsx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1.터파기토사(100%){ (2.00+4.20)÷2} ×        2.20 ×1.0×100%   =6.82 ㎥ 
암(0%){ (2.00+4.20)÷2} ×        2.20 ×1.0×0%   =0.00 ㎥ 
2.되메우기일반토사[ { (2.00+3.70)÷2 ×   1.7 } - { 3.14 × 0.60 ²} ]×1.0  =3.71 ㎥ 
{ (3.70+4.20)÷2} ×        0.50 ×1.0      =1.97 ㎥ 
계 :  =5.68 ㎥ 
 4.잔 토  1식 -3식              =4.85 ㎥ 
+ + + + diff --git a/resources/tester/spreadsheet/fixture_excel_user_won.txt b/resources/tester/spreadsheet/fixture_excel_user_won.txt new file mode 100644 index 00000000..341bff26 --- /dev/null +++ b/resources/tester/spreadsheet/fixture_excel_user_won.txt @@ -0,0 +1,6 @@ + 1.터파기 토사(100%) { ( 2.00 + 4.20 ) ÷ 2 } × 2.20 × 1.0 × 100% = 6.82 ㎥ + 암(0%) { ( 2.00 + 4.20 ) ÷ 2 } × 2.20 × 1.0 × 0% = 0.00 ㎥ +2.되메우기 일반토사 [ { ( 2.00 + 3.70 ) ÷ 2 × 1.7 } - { 3.14 × 0.60 ²} ] × 1.0 = 3.71 ㎥ + { ( 3.70 + 4.20 ) ÷ 2 } × 0.50 × 1.0 = 1.97 ㎥ + 계 : = 5.68 ㎥ + 4.잔 토 1식 - 3식 = 4.85 ㎥ From c5ad429dc3fae6cc2b6fbc2e2ac53c43092896e6 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 28 Sep 2026 13:45:41 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix(spreadsheet):=20=ED=9A=8C=EA=B3=84=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EB=94=B0=EC=98=B4=ED=91=9C=20CSS=20?= =?UTF-8?q?=EC=9C=A0=EB=8B=88=EC=BD=94=EB=93=9C=20=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=ED=94=84(\0022)=20=EC=95=88=20=ED=92=80?= =?UTF-8?q?=EB=A6=AC=EB=8D=98=20=EA=B2=83=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자 실제 엑셀 견본(resources/tester/spreadsheet/fixture_excel_user_won.html, origin/main_desktop_1)으로 붙여넣기 시험 중 회계류 형식(예: `.xl126`)의 `\0022` — 진짜 CSS 유니코드 이스케이프(16진 1~6 자리, 따옴표 `"` 를 이렇게 실음) — 가 한 글자만 풀리고(`\0` → `0`) 나머지 `022` 가 그대로 남아 형식에 글자 "0022" 가 끼던 것 발견 · `unescapeMsoNumFmt` 가 `\x` 한 글자 이스케이프 앞에 `\` + 16진 1~6 자리 이스케이프를 먼저 찾게 고침 - 고친 뒤 회계 4구역 형식이 `_-* #,##0.00_-;\-* #,##0.00_-;_-* "-"??_-;_-@_-` 처럼 온전히 들어옴(글자 "0022" 없음 · 남는 홑 역슬래시 없음 · ₩ 없음) - ORCA 로 이 견본 통째 붙여넣기 확인 — 칸 133개 값 · 병합 9개 · Fixed·0·General· 0% 이름 형식 그대로 넘김 · 「 =」(앞에 공백 있어 원래도 식 아님) · 「1식」· 「3식」 글자 칸 모두 글로 남음(식으로 안 잡힘), 기존 회계 형식 견본 회귀 없음 - 수식 흔적(x:fmla) 없음 재확인 — 이 실 견본에도 x:str/x:num 만 있고 수식은 칸 글 자체가 「=...」 로 시작하는 형태(HTML 클립보드엔 수식이 안 실림) Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AeeQRLknEpP4SFjVkjDWDs --- A00_Common/spreadsheet/spreadsheet_clipboard.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/A00_Common/spreadsheet/spreadsheet_clipboard.ts b/A00_Common/spreadsheet/spreadsheet_clipboard.ts index 06c25067..45c514d4 100644 --- a/A00_Common/spreadsheet/spreadsheet_clipboard.ts +++ b/A00_Common/spreadsheet/spreadsheet_clipboard.ts @@ -254,9 +254,16 @@ function cssColorToHex(v: string | undefined): string | undefined { return undefined; } -/** 엑셀 `mso-number-format:"\#\,\#\#0\.00"` — 따옴표 떼고 `\x` 이스케이프 풀기. */ +/** 엑셀 `mso-number-format:"\#\,\#\#0\.00"` — 따옴표 떼고 `\x` 이스케이프 풀기(`\0022` 처럼 진짜 + * CSS 유니코드 이스케이프(`\` + 16진 1~6 자리, 회계 형식 글자 구역 따옴표 `"` 를 이렇게 실음)도 + * 먼저 풀지 않으면 따옴표 대신 글자 "0022" 가 그대로 남아 형식이 깨짐 — sub5 numfmt 가 받는 + * 형식 코드에는 이 CSS 껍데기가 없어야 함). */ function unescapeMsoNumFmt(v: string): string { - return v.replace(/^"|"$/g, "").replace(/\\(.)/g, "$1"); + return v + .replace(/^"|"$/g, "") + .replace(/\\([0-9a-fA-F]{1,6}) ?|\\(.)/g, (_, hex, ch) => + hex !== undefined ? String.fromCodePoint(parseInt(hex, 16)) : ch, + ); } /** pt 굵기 → 우리 선 종류(엑셀 내보내기 흔한 값 기준 — hairline ≈ .5pt 이하 · thin ≈ 1pt · medium ≈