Merge remote-tracking branches 'origin/sub_laptop_1' and 'origin/sub_laptop_8' into main_laptop_1

This commit is contained in:
2026-09-28 13:58:09 +09:00
11 changed files with 835 additions and 25 deletions
@@ -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(/(?<!\\);/);
}
/** 엑셀 · 구글이 `td style="..."` 에 싣는 인라인 서식 → `CellStyle`(읽을 것 없으면 undefined). */
function parseStyleAttr(styleAttr: string): Record<string, string> {
const out: Record<string, string> = {};
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();
@@ -247,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 ≈
+9 -5
View File
@@ -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;
}
+2 -2
View File
@@ -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
+13 -13
View File
@@ -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"
@@ -0,0 +1,24 @@
<!-- fixture_excel_accounting_fmt.html — 사용자 실제 저장본(material/rv.json)에서 형식 끝에 역슬래시가
남던 흠 재현(브레인 9-28 두 번째 지시). 엑셀 회계 형식은 구역이 넷(양수·음수·0·글자)이라 CSS 로
내보낼 때 구역 나눔 `;` 도 `\;` 로 이스케이프됨 — 선언을 단순히 `;` 로 나누면 이 `\;` 에서 잘려
값 끝에 `\` 만 남고 뒤 구역이 통째로 날아감. `parseStyleAttr`(=`splitDecls`)가 이스케이프된
`;` 를 진짜 선언 나눔과 구별하는지 이 견본으로 시험(실제 엑셀처럼 <style> 클래스에 담음 —
인라인 style 속성에 겹따옴표 낀 값을 두면 prettier 가 CSS 로 다시 찍으며 따옴표 얼개를 바꿔
이 흠 재현 자체가 깨짐). -->
<html>
<head>
<style>
<!--.xl_acct
{mso-number-format:"_-* \#\,\#\#0\.00_-\;\-* \#\,\#\#0\.00_-\;_-* \"-\"??_-\;_-@_-";
text-align:right;}
-->
</style>
</head>
<body>
<table>
<tr>
<td class="xl_acct" x:num>1234.5</td>
</tr>
</table>
</body>
</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
<html xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf-8">
<meta name=ProgId content=Excel.Sheet>
<meta name=Generator content="Microsoft Excel 15">
<link id=Main-File rel=Main-File
href="file:///C:/Users/umsan/AppData/Local/Temp/msohtmlclip1/01/clip.htm">
<link rel=File-List
href="file:///C:/Users/umsan/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml">
<style>
<!--table
{mso-displayed-decimal-separator:"\.";
mso-displayed-thousand-separator:"\,";}
@page
{margin:.68in .28in .39in .6in;
mso-header-margin:.51in;
mso-footer-margin:.31in;
mso-page-orientation:landscape;}
.font5
{color:windowtext;
font-size:8.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:굴림, monospace;
mso-font-charset:129;}
.font6
{color:windowtext;
font-size:8.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:돋움, monospace;
mso-font-charset:129;}
.font7
{color:windowtext;
font-size:8.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:바탕, serif;
mso-font-charset:129;}
tr
{mso-height-source:auto;
mso-ruby-visibility:none;}
col
{mso-width-source:auto;
mso-ruby-visibility:none;}
br
{mso-data-placement:same-cell;}
td
{padding:0px;
mso-ignore:padding;
color:windowtext;
font-size:10.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:굴림, monospace;
mso-font-charset:129;
mso-number-format:General;
text-align:general;
vertical-align:middle;
border:none;
mso-background-source:auto;
mso-pattern:auto;
mso-protection:locked visible;
white-space:nowrap;
mso-rotate:0;}
.xl114
{font-size:12.0pt;
font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;}
.xl115
{font-size:12.0pt;
font-family:굴림체, monospace;
mso-font-charset:129;}
.xl116
{font-family:굴림체, monospace;
mso-font-charset:129;}
.xl117
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:none;
border-left:none;}
.xl118
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:none;
border-left:none;}
.xl119
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;}
.xl120
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:.5pt solid windowtext;
border-bottom:.5pt hairline windowtext;
border-left:.5pt solid windowtext;}
.xl121
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:.5pt solid windowtext;}
.xl122
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:Fixed;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl123
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl124
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.0";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl125
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:0;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl126
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_ * \#\,\#\#0\.00_ \;_ * \\-\#\,\#\#0\.00_ \;_ * \0022-\0022_ \;_ \@_ ";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl127
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:Fixed;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:none;
border-left:none;}
.xl128
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.0";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:none;
border-left:none;}
.xl129
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.00_\)\;\[Red\]\\\(0\.00\\\)";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl130
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:.5pt solid windowtext;
border-right:1.0pt solid windowtext;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl131
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:.5pt solid windowtext;}
.xl132
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:Fixed;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl133
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl134
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0_-\;\\-* \#\,\#\#0_-\;_-* \0022-\0022??_-\;_-\@_-";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl135
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0\.0_-\;\\-* \#\,\#\#0\.0_-\;_-* \0022-\0022??_-\;_-\@_-";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl136
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.0";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl137
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0\.00_-\;\\-* \#\,\#\#0\.00_-\;_-* \0022-\0022??_-\;_-\@_-";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl138
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0\.00_-\;\\-* \#\,\#\#0\.00_-\;_-* \0022-\0022??_-\;_-\@_-";
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl139
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.0";
text-align:left;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl140
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl141
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.0";
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl142
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.00_\)\;\[Red\]\\\(0\.00\\\)";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl143
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:.5pt solid windowtext;
border-right:1.0pt solid windowtext;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl144
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:0;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl145
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_ * \#\,\#\#0\.00_ \;_ * \\-\#\,\#\#0\.00_ \;_ * \0022-\0022_ \;_ \@_ ";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
.xl146
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:left;
border-top:.5pt solid windowtext;
border-right:.5pt solid windowtext;
border-bottom:1.0pt solid windowtext;
border-left:1.0pt solid windowtext;}
.xl147
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:.5pt solid windowtext;
border-bottom:1.0pt solid windowtext;
border-left:.5pt solid windowtext;}
.xl148
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:1.0pt solid windowtext;
border-left:.5pt solid windowtext;}
.xl149
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:Fixed;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:1.0pt solid windowtext;
border-left:none;}
.xl150
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:1.0pt solid windowtext;
border-left:none;}
.xl151
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:1.0pt solid windowtext;
border-left:none;}
.xl152
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.00_\)\;\[Red\]\\\(0\.00\\\)";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:1.0pt solid windowtext;
border-left:none;}
.xl153
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:.5pt solid windowtext;
border-right:1.0pt solid windowtext;
border-bottom:1.0pt solid windowtext;
border-left:none;}
.xl154
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:none;
border-right:none;
border-bottom:none;
border-left:.5pt solid windowtext;}
.xl155
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:Fixed;
text-align:center;}
.xl156
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"0\.0";
text-align:center;}
.xl157
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:0;
text-align:center;}
.xl158
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_ * \#\,\#\#0\.00_ \;_ * \\-\#\,\#\#0\.00_ \;_ * \0022-\0022_ \;_ \@_ ";
text-align:center;}
.xl159
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0\.00_-\;\\-* \#\,\#\#0\.00_-\;_-* \0022-\0022_-\;_-\@_-";
text-align:center;}
.xl160
{font-family:굴림체, monospace;
mso-font-charset:129;
border-top:none;
border-right:1.0pt solid windowtext;
border-bottom:none;
border-left:none;}
.xl161
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:left;
border-top:.5pt solid windowtext;
border-right:.5pt solid windowtext;
border-bottom:none;
border-left:1.0pt solid windowtext;}
.xl162
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:left;
border-top:none;
border-right:.5pt solid windowtext;
border-bottom:.5pt solid windowtext;
border-left:1.0pt solid windowtext;}
.xl163
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0\.00_-\;\\-* \#\,\#\#0\.00_-\;_-* \0022-\0022_-\;_-\@_-";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt hairline windowtext;
border-left:none;}
.xl164
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0\.00_-\;\\-* \#\,\#\#0\.00_-\;_-* \0022-\0022_-\;_-\@_-";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:none;
border-left:none;}
.xl165
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:0%;
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:none;
border-left:none;}
.xl166
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:.5pt solid windowtext;
border-bottom:none;
border-left:1.0pt solid windowtext;}
.xl167
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:none;
border-right:.5pt solid windowtext;
border-bottom:none;
border-left:1.0pt solid windowtext;}
.xl168
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:none;
border-right:.5pt solid windowtext;
border-bottom:.5pt solid windowtext;
border-left:1.0pt solid windowtext;}
.xl169
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:.5pt solid windowtext;
border-right:.5pt solid windowtext;
border-bottom:none;
border-left:.5pt solid windowtext;}
.xl170
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:none;
border-right:.5pt solid windowtext;
border-bottom:none;
border-left:.5pt solid windowtext;}
.xl171
{font-family:굴림체, monospace;
mso-font-charset:129;
text-align:center;
border-top:none;
border-right:.5pt solid windowtext;
border-bottom:.5pt solid windowtext;
border-left:.5pt solid windowtext;}
.xl172
{font-family:굴림체, monospace;
mso-font-charset:129;
mso-number-format:"_-* \#\,\#\#0\.00_-\;\\-* \#\,\#\#0\.00_-\;_-* \0022-\0022_-\;_-\@_-";
text-align:center;
border-top:.5pt solid windowtext;
border-right:none;
border-bottom:.5pt solid windowtext;
border-left:none;}
ruby
{ruby-align:left;}
rt
{color:windowtext;
font-size:8.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:굴림, monospace;
mso-font-charset:129;
mso-char-type:none;
display:none;}
-->
</style>
</head>
<body link=blue vlink=purple>
<table border=0 cellpadding=0 cellspacing=0 width=943 style='border-collapse:
collapse;width:713pt'>
<!--StartFragment-->
<col width=90 style='mso-width-source:userset;mso-width-alt:3291;width:68pt'>
<col width=75 style='mso-width-source:userset;mso-width-alt:2742;width:56pt'>
<col width=44 style='mso-width-source:userset;mso-width-alt:1609;width:33pt'>
<col width=57 style='mso-width-source:userset;mso-width-alt:2084;width:43pt'>
<col width=26 style='mso-width-source:userset;mso-width-alt:950;width:20pt'>
<col width=53 style='mso-width-source:userset;mso-width-alt:1938;width:40pt'>
<col width=37 style='mso-width-source:userset;mso-width-alt:1353;width:28pt'>
<col width=26 style='mso-width-source:userset;mso-width-alt:950;width:20pt'>
<col width=29 span=2 style='mso-width-source:userset;mso-width-alt:1060;
width:22pt'>
<col width=49 style='mso-width-source:userset;mso-width-alt:1792;width:37pt'>
<col width=45 style='mso-width-source:userset;mso-width-alt:1645;width:34pt'>
<col width=50 style='mso-width-source:userset;mso-width-alt:1828;width:38pt'>
<col width=25 style='mso-width-source:userset;mso-width-alt:914;width:19pt'>
<col width=49 style='mso-width-source:userset;mso-width-alt:1792;width:37pt'>
<col width=37 style='mso-width-source:userset;mso-width-alt:1353;width:28pt'>
<col width=20 style='mso-width-source:userset;mso-width-alt:731;width:15pt'>
<col width=26 style='mso-width-source:userset;mso-width-alt:950;width:20pt'>
<col width=32 style='mso-width-source:userset;mso-width-alt:1170;width:24pt'>
<col width=4 style='mso-width-source:userset;mso-width-alt:146;width:3pt'>
<col width=28 style='mso-width-source:userset;mso-width-alt:1024;width:21pt'>
<col width=50 style='mso-width-source:userset;mso-width-alt:1828;width:38pt'>
<col width=37 style='mso-width-source:userset;mso-width-alt:1353;width:28pt'>
<col width=25 style='mso-width-source:userset;mso-width-alt:914;width:19pt'>
<tr height=31 style='mso-height-source:userset;height:23.25pt'>
<td rowspan=2 height=62 class=xl161 width=90 style='border-bottom:.5pt solid black;
height:46.5pt;width:68pt'><span style='mso-spacerun:yes'> </span>1.터파기</td>
<td class=xl120 width=75 style='border-left:none;width:56pt'>토사(100%)</td>
<td class=xl121 width=44 style='border-left:none;width:33pt'>{ (</td>
<td class=xl122 width=57 style='width:43pt'>2.00</td>
<td class=xl123 width=26 style='width:20pt'>+</td>
<td class=xl122 width=53 style='width:40pt'>4.20</td>
<td class=xl123 width=37 style='width:28pt'>)</td>
<td class=xl124 width=26 style='width:20pt'>÷</td>
<td class=xl125 width=29 style='width:22pt'>2</td>
<td class=xl123 width=29 style='width:22pt'>}</td>
<td class=xl126 width=49 style='width:37pt'><span
style='mso-spacerun:yes'> </span>×<span style='mso-spacerun:yes'> </span></td>
<td colspan=2 class=xl163 width=95 style='width:72pt'><span
style='mso-spacerun:yes'>       </span>2.20 </td>
<td class=xl127 width=25 style='width:19pt'>×</td>
<td class=xl128 width=49 style='width:37pt'>1.0</td>
<td class=xl118 width=37 style='width:28pt'>×</td>
<td colspan=2 class=xl165 width=46 style='width:35pt'>100%</td>
<td class=xl117 width=32 style='width:24pt'> </td>
<td class=xl117 width=4 style='width:3pt'> </td>
<td class=xl118 width=28 style='width:21pt'><span
style='mso-spacerun:yes'> </span>=</td>
<td class=xl129 width=50 style='width:38pt'>6.82 </td>
<td class=xl123 width=37 style='width:28pt'>㎥</td>
<td class=xl130 width=25 style='width:19pt'> </td>
</tr>
<tr height=31 style='mso-height-source:userset;height:23.25pt'>
<td height=31 class=xl120 style='height:23.25pt;border-left:none'>암(0%)</td>
<td class=xl121 style='border-left:none'>{ (</td>
<td class=xl122>2.00</td>
<td class=xl123>+</td>
<td class=xl122>4.20</td>
<td class=xl123>)</td>
<td class=xl124>÷</td>
<td class=xl125>2</td>
<td class=xl123>}</td>
<td class=xl126><span style='mso-spacerun:yes'> </span>×<span
style='mso-spacerun:yes'> </span></td>
<td colspan=2 class=xl163><span style='mso-spacerun:yes'>       </span>2.20 </td>
<td class=xl127>×</td>
<td class=xl128>1.0</td>
<td class=xl118>×</td>
<td colspan=2 class=xl165>0%</td>
<td class=xl117> </td>
<td class=xl117> </td>
<td class=xl118><span style='mso-spacerun:yes'> </span>=</td>
<td class=xl129>0.00 </td>
<td class=xl123>㎥</td>
<td class=xl130> </td>
</tr>
<tr height=31 style='mso-height-source:userset;height:23.25pt'>
<td rowspan=3 height=93 class=xl166 style='border-bottom:.5pt solid black;
height:69.75pt;border-top:none'>2.되메우기</td>
<td rowspan=3 class=xl169 style='border-bottom:.5pt solid black'>일반토사</td>
<td class=xl131 style='border-left:none'>[ { (</td>
<td class=xl132>2.00</td>
<td class=xl133>+</td>
<td class=xl132>3.70</td>
<td class=xl133>)</td>
<td class=xl133>÷</td>
<td class=xl133>2</td>
<td class=xl134><span style='mso-spacerun:yes'> </span>×<span
style='mso-spacerun:yes'> </span></td>
<td class=xl135><span style='mso-spacerun:yes'>  </span>1.7 </td>
<td class=xl136>} - {</td>
<td class=xl137><span style='mso-spacerun:yes'> </span>3.14 </td>
<td class=xl136>×</td>
<td class=xl138><span style='mso-spacerun:yes'> </span>0.60 </td>
<td class=xl139>²}<span style='mso-spacerun:yes'> </span></td>
<td class=xl140>]</td>
<td class=xl133>×</td>
<td class=xl136>1.0</td>
<td class=xl141> </td>
<td class=xl133><span style='mso-spacerun:yes'> </span>=</td>
<td class=xl142>3.71 </td>
<td class=xl133>㎥</td>
<td class=xl143> </td>
</tr>
<tr height=31 style='mso-height-source:userset;height:23.25pt'>
<td height=31 class=xl131 style='height:23.25pt;border-top:none;border-left:
none'>{ (</td>
<td class=xl132 style='border-top:none'>3.70</td>
<td class=xl133 style='border-top:none'>+</td>
<td class=xl132 style='border-top:none'>4.20</td>
<td class=xl133 style='border-top:none'>)</td>
<td class=xl136 style='border-top:none'>÷</td>
<td class=xl144 style='border-top:none'>2</td>
<td class=xl133 style='border-top:none'>}</td>
<td class=xl145 style='border-top:none'><span
style='mso-spacerun:yes'> </span>×<span style='mso-spacerun:yes'> </span></td>
<td colspan=2 class=xl172><span style='mso-spacerun:yes'>       </span>0.50 </td>
<td class=xl132 style='border-top:none'>×</td>
<td class=xl136 style='border-top:none'>1.0</td>
<td class=xl140 style='border-top:none'> </td>
<td class=xl140 style='border-top:none'> </td>
<td class=xl136 style='border-top:none'> </td>
<td class=xl140 style='border-top:none'> </td>
<td class=xl140 style='border-top:none'> </td>
<td class=xl133 style='border-top:none'><span
style='mso-spacerun:yes'> </span>=</td>
<td class=xl142 style='border-top:none'>1.97 </td>
<td class=xl133 style='border-top:none'>㎥</td>
<td class=xl143 style='border-top:none'> </td>
</tr>
<tr height=31 style='mso-height-source:userset;height:23.25pt'>
<td height=31 class=xl154 style='height:23.25pt;border-left:none'>계 :<span
style='mso-spacerun:yes'> </span></td>
<td class=xl155></td>
<td class=xl119></td>
<td class=xl155></td>
<td class=xl119></td>
<td class=xl156></td>
<td class=xl157></td>
<td class=xl119></td>
<td class=xl158></td>
<td class=xl159></td>
<td class=xl159></td>
<td class=xl155></td>
<td class=xl156></td>
<td class=xl116></td>
<td class=xl116></td>
<td class=xl156></td>
<td class=xl116></td>
<td class=xl116></td>
<td class=xl133 style='border-top:none'><span
style='mso-spacerun:yes'> </span>=</td>
<td class=xl142 style='border-top:none'>5.68 </td>
<td class=xl133 style='border-top:none'>㎥</td>
<td class=xl160> </td>
</tr>
<tr height=31 style='mso-height-source:userset;height:23.25pt'>
<td height=31 class=xl146 style='height:23.25pt;border-top:none'><span
style='mso-spacerun:yes'> </span>4.잔 토</td>
<td class=xl147 style='border-top:none;border-left:none'> </td>
<td class=xl148 style='border-left:none'> </td>
<td class=xl149>1식</td>
<td class=xl150><span style='mso-spacerun:yes'> </span>-</td>
<td class=xl149>3식</td>
<td class=xl150> </td>
<td colspan=2 class=xl149> </td>
<td class=xl150> </td>
<td class=xl150> </td>
<td class=xl150> </td>
<td class=xl150> </td>
<td class=xl150> </td>
<td class=xl150> </td>
<td class=xl150> </td>
<td class=xl151> </td>
<td class=xl151> </td>
<td class=xl151> </td>
<td class=xl151> </td>
<td class=xl150 style='border-top:none'><span
style='mso-spacerun:yes'> </span>=</td>
<td class=xl152 style='border-top:none'>4.85 </td>
<td class=xl150 style='border-top:none'>㎥</td>
<td class=xl153> </td>
</tr>
<!--EndFragment-->
</table>
</body>
</html>
@@ -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 ㎥
@@ -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_-"),
);
// ── 채우기 ──────────────────────────────────────────────────────────────
@@ -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 가 없음")