kordoc 4.0.1 → 4.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +792 -768
  2. package/dist/{-O3AWNVPH.js → -QSF64HNK.js} +8 -8
  3. package/dist/{chunk-EH2KYFFO.js → chunk-6JSZBSOD.js} +2 -2
  4. package/dist/chunk-6JSZBSOD.js.map +1 -0
  5. package/dist/{chunk-YEC7VG6O.js → chunk-AUJICYJG.js} +3 -3
  6. package/dist/chunk-AUJICYJG.js.map +1 -0
  7. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  8. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  9. package/dist/{chunk-SHVV3BD6.js → chunk-K2SETHJ6.js} +426 -111
  10. package/dist/chunk-K2SETHJ6.js.map +1 -0
  11. package/dist/{chunk-5CJGKKMZ.js → chunk-MEPHGCPQ.js} +1 -1
  12. package/dist/chunk-MEPHGCPQ.js.map +1 -0
  13. package/dist/chunk-MOL7MDBG.js +0 -0
  14. package/dist/{chunk-C5ABJ7PB.js → chunk-MQLKSRIK.js} +3 -3
  15. package/dist/chunk-MQLKSRIK.js.map +1 -0
  16. package/dist/{chunk-NIZXIVEI.cjs → chunk-OVESZY5R.cjs} +2 -2
  17. package/dist/chunk-OVESZY5R.cjs.map +1 -0
  18. package/dist/{chunk-33OUI7HC.js → chunk-PNQDQ3SM.js} +2 -2
  19. package/dist/chunk-PNQDQ3SM.js.map +1 -0
  20. package/dist/{chunk-JMLK6NTW.js → chunk-RO2GVZ3I.js} +3 -3
  21. package/dist/chunk-RO2GVZ3I.js.map +1 -0
  22. package/dist/{chunk-3H3CVOZ3.js → chunk-TQYX5E32.js} +2 -2
  23. package/dist/chunk-TQYX5E32.js.map +1 -0
  24. package/dist/cli.js +37 -15
  25. package/dist/cli.js.map +1 -1
  26. package/dist/{detect-PJZMUL2Z.js → detect-RI2MQ33K.js} +2 -2
  27. package/dist/formula-5NKVS2LR.cjs.map +1 -1
  28. package/dist/formula-JCNF43NE.js +0 -0
  29. package/dist/index.cjs +756 -441
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +60 -6
  32. package/dist/index.d.ts +60 -6
  33. package/dist/index.js +421 -106
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp.js +61 -19
  36. package/dist/mcp.js.map +1 -1
  37. package/dist/page-range-737B4EZW.js +0 -0
  38. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  39. package/dist/{parser-ZOMDHCA7.js → parser-AODAQQLQ.js} +5 -5
  40. package/dist/parser-AODAQQLQ.js.map +1 -0
  41. package/dist/{parser-44OPXZMP.cjs → parser-FMONFTAN.cjs} +16 -16
  42. package/dist/parser-FMONFTAN.cjs.map +1 -0
  43. package/dist/{parser-G433UZMX.js → parser-J7YREDNS.js} +4 -4
  44. package/dist/parser-J7YREDNS.js.map +1 -0
  45. package/dist/{provider-H4WWSPOV.js → provider-4ZJKV3DC.js} +1 -1
  46. package/dist/provider-4ZJKV3DC.js.map +1 -0
  47. package/dist/{provider-7H4CPZYS.js → provider-AKROB7WQ.js} +1 -1
  48. package/dist/provider-AKROB7WQ.js.map +1 -0
  49. package/dist/{provider-5K7O3Z2O.cjs → provider-G4C2V2PD.cjs} +1 -1
  50. package/dist/provider-G4C2V2PD.cjs.map +1 -0
  51. package/dist/render-JPP7YTV3.js +10 -0
  52. package/dist/seal-MDE4DXZQ.js +10 -0
  53. package/dist/setup-57FB3LSP.js +0 -0
  54. package/dist/{watch-UYT3ZCBM.js → watch-P65Z24AL.js} +8 -8
  55. package/dist/watch-P65Z24AL.js.map +1 -0
  56. package/package.json +4 -4
  57. package/dist/chunk-33OUI7HC.js.map +0 -1
  58. package/dist/chunk-3H3CVOZ3.js.map +0 -1
  59. package/dist/chunk-5CJGKKMZ.js.map +0 -1
  60. package/dist/chunk-C5ABJ7PB.js.map +0 -1
  61. package/dist/chunk-EH2KYFFO.js.map +0 -1
  62. package/dist/chunk-JMLK6NTW.js.map +0 -1
  63. package/dist/chunk-NIZXIVEI.cjs.map +0 -1
  64. package/dist/chunk-SHVV3BD6.js.map +0 -1
  65. package/dist/chunk-YEC7VG6O.js.map +0 -1
  66. package/dist/parser-44OPXZMP.cjs.map +0 -1
  67. package/dist/parser-G433UZMX.js.map +0 -1
  68. package/dist/parser-ZOMDHCA7.js.map +0 -1
  69. package/dist/provider-5K7O3Z2O.cjs.map +0 -1
  70. package/dist/provider-7H4CPZYS.js.map +0 -1
  71. package/dist/provider-H4WWSPOV.js.map +0 -1
  72. package/dist/render-SQVQBORJ.js +0 -10
  73. package/dist/seal-6E26L45Z.js +0 -10
  74. package/dist/watch-UYT3ZCBM.js.map +0 -1
  75. /package/dist/{-O3AWNVPH.js.map → -QSF64HNK.js.map} +0 -0
  76. /package/dist/{detect-PJZMUL2Z.js.map → detect-RI2MQ33K.js.map} +0 -0
  77. /package/dist/{render-SQVQBORJ.js.map → render-JPP7YTV3.js.map} +0 -0
  78. /package/dist/{seal-6E26L45Z.js.map → seal-MDE4DXZQ.js.map} +0 -0
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  sanitizeHref,
21
21
  stripDtd,
22
22
  toArrayBuffer
23
- } from "./chunk-3H3CVOZ3.js";
23
+ } from "./chunk-TQYX5E32.js";
24
24
  import {
25
25
  parsePageRange
26
26
  } from "./chunk-GE43BE46.js";
@@ -404,7 +404,7 @@ function createXmlParser(warnings) {
404
404
  return new DOMParser({
405
405
  onError(level, msg2) {
406
406
  if (level === "fatalError") throw new KordocError(`XML \uD30C\uC2F1 \uC2E4\uD328: ${msg2}`);
407
- warnings?.push({ code: "MALFORMED_XML", message: `XML ${level === "warn" ? "\uACBD\uACE0" : "\uC624\uB958"}: ${msg2}` });
407
+ warnings?.push({ code: "MALFORMED_XML", message: `XML ${level === "warning" ? "\uACBD\uACE0" : "\uC624\uB958"}: ${msg2}` });
408
408
  }
409
409
  });
410
410
  }
@@ -463,9 +463,9 @@ async function extractHwpxStyles(zip, decompressed) {
463
463
  const parser = createXmlParser();
464
464
  const doc = parser.parseFromString(stripDtd(xml), "text/xml");
465
465
  if (!doc.documentElement) continue;
466
- parseCharProperties(doc, result.charProperties);
467
- parseStyleElements(doc, result.styles);
468
466
  const domDoc = doc;
467
+ parseCharProperties(domDoc, result.charProperties);
468
+ parseStyleElements(domDoc, result.styles);
469
469
  parseNumberings(domDoc, result.numberings);
470
470
  parseBullets(domDoc, result.bullets);
471
471
  parseParaHeadings(domDoc, result.paraHeadings);
@@ -1333,12 +1333,17 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1333
1333
  break;
1334
1334
  }
1335
1335
  // 표/도표 캡션 — IRTable.caption으로 보존 (v3.0, 기존 무음 드롭 수정)
1336
- case "caption":
1337
- if (tableCtx) {
1338
- const capText = collectSubListText(el, ctx);
1339
- if (capText) tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + capText;
1336
+ case "caption": {
1337
+ const capText = collectSubListText(el, ctx);
1338
+ if (capText) {
1339
+ if (tableCtx) {
1340
+ tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + capText;
1341
+ } else {
1342
+ blocks.push({ type: "paragraph", text: capText, pageNumber: ctx.sectionNum });
1343
+ }
1340
1344
  }
1341
1345
  break;
1346
+ }
1342
1347
  case "tr":
1343
1348
  if (tableCtx) {
1344
1349
  tableCtx.currentRow = [];
@@ -1503,7 +1508,7 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1503
1508
  if (depth > MAX_XML_DEPTH) return tableCtx;
1504
1509
  const children = node.childNodes;
1505
1510
  if (!children) return tableCtx;
1506
- const walkChildren = (parent, d) => {
1511
+ const walkChildren = (parent, d, inShape = false) => {
1507
1512
  if (d > MAX_XML_DEPTH) return;
1508
1513
  const kids2 = parent.childNodes;
1509
1514
  if (!kids2) return;
@@ -1517,6 +1522,13 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1517
1522
  const newTable = { rows: [], currentRow: [], cell: null };
1518
1523
  walkSection(el, blocks, newTable, tableStack, ctx, d + 1);
1519
1524
  tableCtx = completeTable(newTable, tableStack, blocks, ctx);
1525
+ } else if (localTag === "caption" && !inShape) {
1526
+ const capText = collectSubListText(el, ctx);
1527
+ if (capText) {
1528
+ if (tableCtx?.cell) mergeBlocksIntoCell(tableCtx.cell, [{ type: "paragraph", text: capText, pageNumber: ctx.sectionNum }]);
1529
+ else if (tableCtx) tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + capText;
1530
+ else blocks.push({ type: "paragraph", text: capText, pageNumber: ctx.sectionNum });
1531
+ }
1520
1532
  } else if (localTag === "pic" || localTag === "shape" || localTag === "drawingObject") {
1521
1533
  if (tableCtx?.cell) {
1522
1534
  const sink = [];
@@ -1533,8 +1545,10 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1533
1545
  } else {
1534
1546
  extractDrawTextBlocks(el, blocks, ctx);
1535
1547
  }
1536
- } else if (localTag === "r" || localTag === "run" || localTag === "ctrl" || localTag === "rect" || localTag === "ellipse" || localTag === "polygon" || localTag === "line" || localTag === "arc" || localTag === "curve" || localTag === "connectLine" || localTag === "container") {
1537
- walkChildren(el, d + 1);
1548
+ } else if (localTag === "r" || localTag === "run" || localTag === "ctrl") {
1549
+ walkChildren(el, d + 1, inShape);
1550
+ } else if (localTag === "rect" || localTag === "ellipse" || localTag === "polygon" || localTag === "line" || localTag === "arc" || localTag === "curve" || localTag === "connectLine" || localTag === "container") {
1551
+ walkChildren(el, d + 1, true);
1538
1552
  }
1539
1553
  }
1540
1554
  };
@@ -1670,6 +1684,9 @@ function extractParagraphInfo(para2, styleMap, ctx) {
1670
1684
  case "indexmark":
1671
1685
  case "colPr":
1672
1686
  break;
1687
+ // 캡션 — walkParagraphChildren의 caption 분기가 보존하므로 손실 경고 대상 아님
1688
+ case "caption":
1689
+ break;
1673
1690
  // 미지원 요소 — 텍스트를 가졌으면 무음 손실 대신 경고
1674
1691
  default: {
1675
1692
  if (ctx?.warnings && extractTextFromNode(k)) {
@@ -20951,7 +20968,8 @@ function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, m
20951
20968
 
20952
20969
  // src/hwpx/gaejosik.ts
20953
20970
  var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
20954
- function gaejosikMarker(depth) {
20971
+ function gaejosikMarker(depth, bullet2 = "\u25CB") {
20972
+ if (depth === 1) return bullet2;
20955
20973
  return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
20956
20974
  }
20957
20975
  function gaejosikSizes(bodyHeight, ov = {}) {
@@ -20987,11 +21005,11 @@ function gaejosikSpaceBefore(depth, bodyHeight) {
20987
21005
  const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
20988
21006
  return Math.round(ui * s);
20989
21007
  }
20990
- function gaejosikLevelIndent(depth, bodyHeight, sizes = {}) {
21008
+ function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
20991
21009
  const lefts = [0, 1, 1.5, 2];
20992
21010
  const left = depth <= 3 ? Math.round(lefts[depth] * bodyHeight) : Math.round((2 + (depth - 3) * 0.5) * bodyHeight);
20993
21011
  const h = depth === 0 ? gaejosikSizes(bodyHeight, sizes).dae : bodyHeight;
20994
- return { left, indent: -markerWidth(gaejosikMarker(depth), h) };
21012
+ return { left, indent: -markerWidth(gaejosikMarker(depth, bullet2), h) };
20995
21013
  }
20996
21014
  function gaejosikChamIndent(bodyHeight, sizes = {}) {
20997
21015
  const cham = gaejosikSizes(bodyHeight, sizes).cham;
@@ -21059,16 +21077,20 @@ function bodyTitleGeom(bodyHeight, ov = {}) {
21059
21077
  }
21060
21078
 
21061
21079
  // src/hwpx/gongmun.ts
21062
- var OFFICIAL_MARGINS = { top: 20, bottom: 10, left: 20, right: 20 };
21080
+ var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
21063
21081
  var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
21064
21082
  var GAEJOSIK_HEADER_FOOTER = 4251;
21065
21083
  var PRESET_DEFAULTS = {
21066
- official: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
21084
+ // 서울시 실결재 104건 12pt 64건(13pt 30, 15pt 5) — 지배값을 기본으로 사용.
21085
+ official: { bodyPt: 12, lineSpacing: 160, numbering: "standard" },
21067
21086
  report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
21068
- plan: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
21087
+ // 실측 추진계획안: * 계층.
21088
+ plan: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
21069
21089
  notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
21070
21090
  minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
21071
- gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" }
21091
+ gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
21092
+ // 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
21093
+ press: { bodyPt: 14, lineSpacing: 160, numbering: "report" }
21072
21094
  };
21073
21095
  var PRESET_ALIAS = {
21074
21096
  official: "official",
@@ -21091,7 +21113,9 @@ var PRESET_ALIAS = {
21091
21113
  \uAC1C\uC870\uC2DD: "gaejosik",
21092
21114
  \uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
21093
21115
  \uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
21094
- \uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik"
21116
+ \uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
21117
+ press: "press",
21118
+ \uBCF4\uB3C4\uC790\uB8CC: "press"
21095
21119
  };
21096
21120
  function normalizeGongmunPreset(preset) {
21097
21121
  if (!preset) return "official";
@@ -21100,7 +21124,46 @@ function normalizeGongmunPreset(preset) {
21100
21124
  function usesReportFonts(preset) {
21101
21125
  return preset === "gaejosik" || preset === "report" || preset === "plan";
21102
21126
  }
21127
+ function usesAsteriskThird(preset) {
21128
+ return preset === "plan" || preset === "press";
21129
+ }
21130
+ function needsGaejosikAssets(gongmun) {
21131
+ return usesReportFonts(gongmun.preset) || gongmun.cover !== null || gongmun.toc;
21132
+ }
21133
+ function assertFiniteRange(name, value, min, max) {
21134
+ if (!Number.isFinite(value) || value < min || value > max) {
21135
+ throw new KordocError(`${name} must be a finite number between ${min} and ${max}`);
21136
+ }
21137
+ }
21138
+ function validateGongmunOptions(opts) {
21139
+ if (opts.bodyPt !== void 0) assertFiniteRange("bodyPt", opts.bodyPt, 6, 40);
21140
+ if (opts.lineSpacing !== void 0) assertFiniteRange("lineSpacing", opts.lineSpacing, 50, 300);
21141
+ if (typeof opts.autoFit === "object" && opts.autoFit.minRatio !== void 0) {
21142
+ assertFiniteRange("autoFit.minRatio", opts.autoFit.minRatio, 50, 99);
21143
+ }
21144
+ if (opts.margins) {
21145
+ for (const side of ["top", "bottom", "left", "right"]) {
21146
+ const value = opts.margins[side];
21147
+ assertFiniteRange(`margins.${side}`, value, 0, 200);
21148
+ }
21149
+ if (opts.margins.left + opts.margins.right >= 210) {
21150
+ throw new KordocError("margins.left + margins.right must be less than 210");
21151
+ }
21152
+ if (opts.margins.top + opts.margins.bottom >= 297) {
21153
+ throw new KordocError("margins.top + margins.bottom must be less than 297");
21154
+ }
21155
+ }
21156
+ if (opts.sizes) {
21157
+ for (const [key, value] of Object.entries(opts.sizes)) {
21158
+ if (value !== void 0) assertFiniteRange(`sizes.${key}`, value, 6, 60);
21159
+ }
21160
+ }
21161
+ if (opts.approval && opts.approval.length > 6) {
21162
+ throw new KordocError("approval must contain at most 6 labels");
21163
+ }
21164
+ }
21103
21165
  function resolveGongmun(opts) {
21166
+ validateGongmunOptions(opts);
21104
21167
  const preset = normalizeGongmunPreset(opts.preset);
21105
21168
  const d = PRESET_DEFAULTS[preset];
21106
21169
  const bodyPt = opts.bodyPt ?? d.bodyPt;
@@ -21108,29 +21171,44 @@ function resolveGongmun(opts) {
21108
21171
  const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik";
21109
21172
  const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
21110
21173
  const gaejosik = preset === "gaejosik";
21174
+ const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
21111
21175
  return {
21112
21176
  preset,
21113
21177
  bodyFont: opts.bodyFont ?? "myeongjo",
21114
21178
  bodyHeight: Math.round(bodyPt * 100),
21115
21179
  lineSpacing: opts.lineSpacing ?? d.lineSpacing,
21116
21180
  numbering: opts.numbering ?? d.numbering,
21117
- margins: opts.margins ?? (gaejosik ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
21181
+ margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
21118
21182
  centerTitle: opts.centerTitle ?? true,
21119
21183
  autoFitMinRatio,
21120
- cover: coverOn ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
21121
- toc: opts.toc ?? gaejosik,
21184
+ // 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가
21185
+ // 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
21186
+ cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
21187
+ toc: preset !== "press" && (opts.toc ?? gaejosik),
21122
21188
  fonts: opts.fonts ?? {},
21123
21189
  sizes: opts.sizes ?? {},
21124
21190
  // 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
21125
21191
  pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
21126
- headerFooter: gaejosik ? GAEJOSIK_HEADER_FOOTER : 0,
21192
+ // 머리말·꼬리말 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
21193
+ // 기안문 0(실결재 41/60건 h0/f0)
21194
+ headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
21127
21195
  // "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
21128
21196
  endMark: opts.endMark ?? preset === "official",
21129
21197
  approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
21130
21198
  // 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
21131
21199
  bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
21132
- // h2 말머리 — 실측 보고서 양식(부처별 3종)의 섹션 제목이 곧 □ 대항목 (QA-2)
21133
- h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : "none")
21200
+ // h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
21201
+ h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : preset === "notice" ? "number" : "none"),
21202
+ // 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
21203
+ bullet2: opts.bullet2 ?? (preset === "plan" || preset === "notice" || preset === "press" ? "\u3147" : "\u25CB"),
21204
+ // 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
21205
+ suppressSingle: opts.suppressSingle ?? false,
21206
+ docHead: preset === "official" && opts.docHead ? opts.docHead : null,
21207
+ docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
21208
+ reportInfo: opts.reportInfo?.trim() || null,
21209
+ noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
21210
+ // 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
21211
+ press: preset === "press" ? opts.press ?? {} : null
21134
21212
  };
21135
21213
  }
21136
21214
  var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
@@ -21148,6 +21226,7 @@ function circledHangul(n) {
21148
21226
  return String.fromCodePoint(12910 + n % 14);
21149
21227
  }
21150
21228
  var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
21229
+ var ASTERISK_BULLETS = ["\u25A1", "\u25CB", "*", "\u318D"];
21151
21230
  function standardMarker(depth, n) {
21152
21231
  switch (depth) {
21153
21232
  case 0:
@@ -21170,17 +21249,19 @@ function standardMarker(depth, n) {
21170
21249
  return circledHangul(n);
21171
21250
  }
21172
21251
  }
21173
- function reportMarker(depth) {
21174
- return REPORT_BULLETS[Math.min(depth, REPORT_BULLETS.length - 1)];
21252
+ function reportMarker(depth, bullet2 = "\u25CB", asteriskThird = false) {
21253
+ const bullets = asteriskThird ? ASTERISK_BULLETS : REPORT_BULLETS;
21254
+ const m = bullets[Math.min(depth, bullets.length - 1)];
21255
+ return depth === 1 ? bullet2 : m;
21175
21256
  }
21176
21257
  function markerWidth(marker, bodyHeight) {
21177
21258
  let em = SPACE_EM_FIXED;
21178
21259
  for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
21179
21260
  return Math.round(em / 1e3 * bodyHeight);
21180
21261
  }
21181
- function levelIndent(depth, bodyHeight, numbering, sizes = {}) {
21182
- if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes);
21183
- const marker = numbering === "report" ? reportMarker(depth) : standardMarker(depth, 0);
21262
+ function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false) {
21263
+ if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
21264
+ const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
21184
21265
  return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, bodyHeight) };
21185
21266
  }
21186
21267
  function computeSuppression(depths) {
@@ -21197,8 +21278,10 @@ function computeSuppression(depths) {
21197
21278
  return keys.map((k) => (counts.get(k) ?? 0) <= 1);
21198
21279
  }
21199
21280
  var GongmunNumberer = class {
21200
- constructor(numbering) {
21281
+ constructor(numbering, bullet2 = "\u25CB", asteriskThird = false) {
21201
21282
  this.numbering = numbering;
21283
+ this.bullet2 = bullet2;
21284
+ this.asteriskThird = asteriskThird;
21202
21285
  }
21203
21286
  counts = [];
21204
21287
  /** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
@@ -21207,8 +21290,8 @@ var GongmunNumberer = class {
21207
21290
  const n = this.counts[depth] ?? 0;
21208
21291
  this.counts[depth] = n + 1;
21209
21292
  if (suppress) return "";
21210
- if (this.numbering === "gaejosik") return gaejosikMarker(depth);
21211
- return this.numbering === "report" ? reportMarker(depth) : standardMarker(depth, n);
21293
+ if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
21294
+ return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.asteriskThird) : standardMarker(depth, n);
21212
21295
  }
21213
21296
  reset() {
21214
21297
  this.counts = [];
@@ -21324,12 +21407,16 @@ var GJ_CHAR_TABLE = 22;
21324
21407
  var GJ_CHAR_TABLE_BOLD = 23;
21325
21408
  var GJ_CHAR_BAR = 24;
21326
21409
  var GJ_CHAR_BODY_TITLE = 25;
21327
- var GJ_CHAR_COUNT = 15;
21410
+ var GJ_CHAR_TITLE_BAR = 26;
21411
+ var GJ_CHAR_APPROVAL = 27;
21412
+ var GJ_CHAR_COUNT = 17;
21328
21413
  var GJ_PARA_CHAM = 20;
21329
21414
  var GJ_PARA_COVER = 21;
21330
21415
  var GJ_PARA_TOC_ITEM = 22;
21331
21416
  var GJ_PARA_CHAPTER = 23;
21332
21417
  var GJ_PARA_BAR = 24;
21418
+ var GONGMUN_LIST_PLAIN_BASE = 25;
21419
+ var GONGMUN_PARA_APPROVAL = GONGMUN_LIST_PLAIN_BASE + GONGMUN_LIST_LEVELS;
21333
21420
  var GJ_BF_CHAPTER_NUM = 3;
21334
21421
  var GJ_BF_CHAPTER_GAP = 4;
21335
21422
  var GJ_BF_CHAPTER_TITLE = 5;
@@ -21337,8 +21424,8 @@ var GJ_BF_BAR_DARK = 6;
21337
21424
  var GJ_BF_BAR_LIGHT = 7;
21338
21425
  var GJ_BF_TOC_BOX = 8;
21339
21426
  var GJ_BF_TOC_STRIPE = 9;
21340
- function gongmunTableHeaderBf(gaejosik) {
21341
- return gaejosik ? 10 : 3;
21427
+ function gongmunTableHeaderBf(richAssets) {
21428
+ return richAssets ? 10 : 3;
21342
21429
  }
21343
21430
  function borderFillEntry(id, b, fill) {
21344
21431
  const side = (name, v) => v ? ` <hh:${name} type="${v[2] ?? "SOLID"}" width="${v[0]}" color="${v[1]}"/>` : ` <hh:${name} type="NONE" width="0.1 mm" color="#000000"/>`;
@@ -21368,9 +21455,15 @@ function newPageNumCtrl(num4 = 1) {
21368
21455
  function pageHidingCtrl(hideHeader = false) {
21369
21456
  return `<hp:ctrl><hp:pageHiding hideHeader="${hideHeader ? 1 : 0}" hideFooter="0" hideMasterPage="0" hideBorder="0" hideFill="0" hidePageNum="1"/></hp:ctrl>`;
21370
21457
  }
21458
+ var GONGMUN_TBL_CHAR = 11;
21459
+ var GONGMUN_TBL_CHAR_BOLD = 12;
21460
+ var GONGMUN_TBL_PT = 1200;
21461
+ var GONGMUN_TITLE_BAR_CHAR = 13;
21462
+ var GONGMUN_APPROVAL_CHAR = 14;
21371
21463
  var CHAR_VARIANT_BASE = 11;
21372
- function charVariantBase(measured) {
21373
- return measured ? CHAR_VARIANT_BASE + GJ_CHAR_COUNT : CHAR_VARIANT_BASE;
21464
+ function charVariantBase(richAssets, isGongmun = true) {
21465
+ if (!isGongmun) return CHAR_VARIANT_BASE;
21466
+ return richAssets ? 11 + GJ_CHAR_COUNT : 11 + 4;
21374
21467
  }
21375
21468
  var GONGMUN_BODY_RATIO = 95;
21376
21469
 
@@ -21619,6 +21712,7 @@ function generateManifest(chartParts = []) {
21619
21712
  }
21620
21713
  function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
21621
21714
  const measured = !!gongmun && usesReportFonts(gongmun.preset);
21715
+ const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
21622
21716
  let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
21623
21717
  if (gongmun) {
21624
21718
  body = gongmun.bodyHeight;
@@ -21626,9 +21720,9 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
21626
21720
  h1 = gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700;
21627
21721
  h2 = 1600;
21628
21722
  h3 = body;
21629
- h4 = Math.max(body - 100, 1300);
21723
+ h4 = Math.min(h3, Math.max(body - 100, 1300));
21630
21724
  }
21631
- const bodyFont = measured ? 4 : 0;
21725
+ const bodyFont = richAssets ? 4 : 0;
21632
21726
  const hFont = measured ? 3 : 1;
21633
21727
  const hBold = !measured;
21634
21728
  const bodyRatio = gongmun ? GONGMUN_BODY_RATIO : 100;
@@ -21645,7 +21739,15 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
21645
21739
  charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, bodyFont, theme.tableHeader),
21646
21740
  charPr(CHAR_QUOTE, body, false, true, bodyFont, theme.quote)
21647
21741
  ];
21648
- if (measured) {
21742
+ if (gongmun && !richAssets) {
21743
+ rows.push(
21744
+ charPr(11, GONGMUN_TBL_PT, false, false, bodyFont, theme.body),
21745
+ charPr(12, GONGMUN_TBL_PT, true, false, bodyFont, theme.body),
21746
+ charPr(GONGMUN_TITLE_BAR_CHAR, 100, false, false, bodyFont, theme.body),
21747
+ charPr(GONGMUN_APPROVAL_CHAR, 1e3, false, false, bodyFont, theme.body)
21748
+ );
21749
+ }
21750
+ if (richAssets) {
21649
21751
  const sz = gaejosikSizes(body, gongmun.sizes);
21650
21752
  rows.push(
21651
21753
  charPr(11, sz.dae, false, false, 3),
@@ -21676,8 +21778,12 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
21676
21778
  // 표 셀 bold
21677
21779
  charPr(24, sz.bar, false, false, 4),
21678
21780
  // 표지 바 셀 빈 문단(6pt)
21679
- charPr(25, sz.bodyTitle, false, false, 3)
21781
+ charPr(25, sz.bodyTitle, false, false, 3),
21680
21782
  // 본문 제목박스(HY헤드라인M 22pt, 실측 GT3 표④)
21783
+ charPr(GJ_CHAR_TITLE_BAR, 100, false, false, 4),
21784
+ // 1페이지형 제목박스 바(1pt)
21785
+ charPr(GJ_CHAR_APPROVAL, 1e3, false, false, 7)
21786
+ // 결재란 라벨(맑은 고딕 10pt)
21681
21787
  );
21682
21788
  }
21683
21789
  for (const r of ratioVariants) {
@@ -21723,16 +21829,16 @@ ${base2.join("\n")}
21723
21829
  paraPr(7, { align: "LEFT", lineSpacing: ls, indent: 600, keepWord: true })
21724
21830
  ];
21725
21831
  for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
21726
- const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes);
21727
- const sectionGap = gongmun.numbering === "gaejosik" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : gongmun.numbering === "report" && d === 0 ? Math.round(gongmun.bodyHeight * 0.5) : 0;
21728
- const keepNext = gongmun.numbering === "gaejosik" && d === 0;
21832
+ const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
21833
+ const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : 0;
21834
+ const keepNext = (gongmun.numbering === "gaejosik" || gongmun.numbering === "report") && d === 0;
21729
21835
  base.push(paraPr(GONGMUN_LIST_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent, spaceBefore: sectionGap, keepWord: true, keepWithNext: keepNext }));
21730
21836
  }
21731
21837
  base.push(paraPr(GONGMUN_CENTER, { align: "CENTER", lineSpacing: ls, keepWord: true }));
21732
21838
  base.push(paraPr(GONGMUN_RIGHT, { align: "RIGHT", lineSpacing: ls, keepWord: true }));
21733
21839
  base.push(paraPr(GONGMUN_TBL_CENTER, { align: "CENTER", lineSpacing: 130, keepWord: true }));
21734
21840
  base.push(paraPr(GONGMUN_TBL_LEFT, { align: "LEFT", lineSpacing: 130, keepWord: true }));
21735
- if (gongmun.preset === "gaejosik") {
21841
+ {
21736
21842
  const cham = gaejosikChamIndent(gongmun.bodyHeight, gongmun.sizes);
21737
21843
  const toc = gaejosikTocItemIndent(gongmun.bodyHeight, gongmun.sizes);
21738
21844
  base.push(
@@ -21744,12 +21850,13 @@ ${base2.join("\n")}
21744
21850
  // 표지 장식 바 셀 빈 문단 — 저줄간격(실측 71%)으로 바 높이 818 안에 수납
21745
21851
  paraPr(GJ_PARA_BAR, { align: "CENTER", lineSpacing: 70, keepWord: true })
21746
21852
  );
21747
- } else if (usesReportFonts(gongmun.preset)) {
21748
- const cham = gaejosikChamIndent(gongmun.bodyHeight, gongmun.sizes);
21749
- base.push(
21750
- paraPr(GJ_PARA_CHAM, { align: "JUSTIFY", lineSpacing: ls, left: cham.left, indent: cham.indent, spaceBefore: gaejosikSpaceBefore(3, gongmun.bodyHeight), keepWord: true })
21751
- );
21752
21853
  }
21854
+ for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
21855
+ const { left } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
21856
+ const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : 0;
21857
+ base.push(paraPr(GONGMUN_LIST_PLAIN_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent: 0, spaceBefore: sectionGap, keepWord: true }));
21858
+ }
21859
+ base.push(paraPr(GONGMUN_PARA_APPROVAL, { align: "CENTER", lineSpacing: 100, keepWord: true }));
21753
21860
  return `<hh:paraProperties itemCnt="${base.length}">
21754
21861
  ${base.join("\n")}
21755
21862
  </hh:paraProperties>`;
@@ -21766,14 +21873,14 @@ ${heads}
21766
21873
  </hh:numberings>`;
21767
21874
  }
21768
21875
  function fontEntry(id, face, weight = 6) {
21769
- return ` <hh:font id="${id}" face="${face}" type="TTF" isEmbedded="0">
21876
+ return ` <hh:font id="${id}" face="${escapeXml(face)}" type="TTF" isEmbedded="0">
21770
21877
  <hh:typeInfo familyType="FCAT_GOTHIC" weight="${weight}" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
21771
21878
  </hh:font>`;
21772
21879
  }
21773
21880
  function buildFontFaces(gongmun, bodyFace) {
21774
- if (gongmun && usesReportFonts(gongmun.preset)) {
21881
+ if (gongmun && needsGaejosikAssets(gongmun)) {
21775
21882
  const ov = gongmun.fonts;
21776
- const bodyDefault = gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870";
21883
+ const bodyDefault = usesReportFonts(gongmun.preset) ? gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870" : bodyFace;
21777
21884
  const faces = [
21778
21885
  "\uD568\uCD08\uB86C\uBC14\uD0D5",
21779
21886
  "\uD568\uCD08\uB86C\uB3CB\uC6C0",
@@ -21832,7 +21939,7 @@ function buildBorderFills(gongmun, extra = []) {
21832
21939
  borderFillEntry(1, {}),
21833
21940
  borderFillEntry(2, { l: thin, r: thin, t: thin, b: thin })
21834
21941
  ];
21835
- if (gongmun?.preset === "gaejosik") {
21942
+ if (gongmun && needsGaejosikAssets(gongmun)) {
21836
21943
  const c = GAEJOSIK_COLORS;
21837
21944
  const edge = ["0.12 mm", c.border];
21838
21945
  const grayLine = ["0.1 mm", c.titleLine];
@@ -22129,7 +22236,9 @@ function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
22129
22236
  if (gongmun.numbering === "gaejosik" && (depth === 0 || (block.text || "").trimStart().startsWith("\u203B"))) continue;
22130
22237
  const content = plainRenderText(block.text || "");
22131
22238
  text = marker ? `${marker} ${content}` : content;
22132
- const { left, indent } = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering);
22239
+ const li = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
22240
+ const left = li.left;
22241
+ const indent = marker ? li.indent : 0;
22133
22242
  firstW = pageW - left - Math.max(indent, 0);
22134
22243
  contW = pageW - left - Math.max(-indent, 0);
22135
22244
  } else if (block.type === "paragraph") {
@@ -22157,7 +22266,7 @@ function variantMapper(fit, blockIdx, base = CHAR_VARIANT_BASE) {
22157
22266
  }
22158
22267
  function precomputeGongmunList(blocks, gongmun) {
22159
22268
  const result = /* @__PURE__ */ new Map();
22160
- const depthOffset = gongmun.numbering === "gaejosik" && blocks.some((b) => b.type === "heading" && b.level === 3) ? 1 : 0;
22269
+ const depthOffset = gongmun.numbering === "gaejosik" && blocks.some((b) => b.type === "heading" && b.level === 3) || gongmun.numbering === "standard" && gongmun.h2Marker === "number" && blocks.some((b) => b.type === "heading" && b.level === 2) ? 1 : 0;
22161
22270
  let i = 0;
22162
22271
  while (i < blocks.length) {
22163
22272
  if (blocks[i].type !== "list_item") {
@@ -22184,8 +22293,8 @@ function precomputeGongmunList(blocks, gongmun) {
22184
22293
  break;
22185
22294
  }
22186
22295
  const depths = run.map((bi) => Math.min(Math.max((blocks[bi].indent || 0) + depthOffset, 0), GONGMUN_LIST_LEVELS - 1));
22187
- const suppress = gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
22188
- const numberer = new GongmunNumberer(gongmun.numbering);
22296
+ const suppress = gongmun.suppressSingle && gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
22297
+ const numberer = new GongmunNumberer(gongmun.numbering, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
22189
22298
  run.forEach((bi, k) => {
22190
22299
  const marker = numberer.next(depths[k], suppress[k]);
22191
22300
  result.set(bi, { marker, depth: depths[k] });
@@ -22215,7 +22324,8 @@ function cell(opts) {
22215
22324
  return `<hp:tc name="" header="0" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${opts.bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="${opts.vertAlign ?? "CENTER"}" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${paras}</hp:subList><hp:cellAddr colAddr="${opts.col}" rowAddr="${opts.row ?? 0}"/><hp:cellSpan colSpan="${opts.colSpan ?? 1}" rowSpan="1"/><hp:cellSz width="${opts.w}" height="${opts.h}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
22216
22325
  }
22217
22326
  function table(rows, w, h, cols, tblBf = 1) {
22218
- return `<hp:tbl id="${++gjTableId}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="CELL" repeatHeader="0" rowCnt="${rows.length}" colCnt="${cols}" cellSpacing="0" borderFillIDRef="${tblBf}" noAdjust="1"><hp:sz width="${w}" widthRelTo="ABSOLUTE" height="${h}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="1" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="283" right="283" top="283" bottom="283"/><hp:inMargin left="141" right="141" top="141" bottom="141"/>` + rows.map((r) => `<hp:tr>${r}</hp:tr>`).join("") + `</hp:tbl>`;
22327
+ const outLR = w + 566 > 48e3 ? 0 : 283;
22328
+ return `<hp:tbl id="${++gjTableId}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="CELL" repeatHeader="0" rowCnt="${rows.length}" colCnt="${cols}" cellSpacing="0" borderFillIDRef="${tblBf}" noAdjust="1"><hp:sz width="${w}" widthRelTo="ABSOLUTE" height="${h}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="1" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="${outLR}" right="${outLR}" top="283" bottom="283"/><hp:inMargin left="141" right="141" top="141" bottom="141"/>` + rows.map((r) => `<hp:tr>${r}</hp:tr>`).join("") + `</hp:tbl>`;
22219
22329
  }
22220
22330
  function buildGaejosikCover(title, gongmun) {
22221
22331
  const g = coverGeom(gongmun.bodyHeight, gongmun.sizes);
@@ -22878,7 +22988,7 @@ function profileColWidths(tp, colCnt) {
22878
22988
  if (tp.width) return Array(colCnt).fill(Math.floor(tp.width / colCnt));
22879
22989
  return null;
22880
22990
  }
22881
- var CELL_PAD = 582;
22991
+ var CELL_PAD = 1200;
22882
22992
  function cellContentWidth(text, charHeight) {
22883
22993
  let max = 0;
22884
22994
  for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
@@ -22887,35 +22997,51 @@ function cellContentWidth(text, charHeight) {
22887
22997
  }
22888
22998
  return max;
22889
22999
  }
22890
- function computeColWidths(colMax, totalWidth) {
23000
+ function cellMinWordWidth(text, charHeight) {
23001
+ let max = 0;
23002
+ for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
23003
+ for (const word of seg.trim().split(/\s+/)) {
23004
+ const w = measureTextWidth(word, charHeight, 100);
23005
+ if (w > max) max = w;
23006
+ }
23007
+ }
23008
+ return max;
23009
+ }
23010
+ function computeColWidths(colMax, totalWidth, colMinWord = []) {
22891
23011
  const colCnt = colMax.length;
22892
23012
  const minW = Math.min(Math.max(2e3, Math.round(totalWidth * 0.06)), Math.floor(totalWidth / colCnt));
22893
23013
  const cap = Math.round(totalWidth * 0.8);
22894
23014
  const raw = colMax.map((w) => Math.min(Math.max(w + CELL_PAD, minW), cap));
23015
+ const floor = raw.map((r, i) => Math.min(Math.max(minW, (colMinWord[i] ?? 0) + CELL_PAD), r, cap));
22895
23016
  const widths = Array(colCnt).fill(0);
22896
23017
  const free = new Set(raw.map((_, i) => i));
22897
23018
  let budget = totalWidth;
22898
- const evenShare = Math.floor(totalWidth / colCnt);
22899
- for (let i = 0; i < colCnt; i++) {
22900
- const fixed = Math.min(Math.round(raw[i] * 1.12), evenShare);
22901
- if (raw[i] <= evenShare && free.size > 1) {
22902
- widths[i] = fixed;
22903
- free.delete(i);
22904
- budget -= fixed;
22905
- }
22906
- }
22907
- if (free.size === 0) {
23019
+ let sumFloorFree = floor.reduce((a, b) => a + b, 0);
23020
+ for (const i of [...raw.keys()].sort((a, b) => raw[a] - raw[b])) {
23021
+ if (free.size <= 1) break;
23022
+ const fixed = Math.round(raw[i] * 1.12);
23023
+ if (budget - fixed < sumFloorFree - floor[i]) break;
23024
+ widths[i] = fixed;
23025
+ free.delete(i);
23026
+ budget -= fixed;
23027
+ sumFloorFree -= floor[i];
23028
+ }
23029
+ const sumFreeRaw = [...free].reduce((a, i) => a + raw[i], 0);
23030
+ if (budget > sumFreeRaw * 1.6) {
22908
23031
  const sumRaw = raw.reduce((a, b) => a + b, 0);
22909
- for (let i = 0; i < colCnt; i++) widths[i] += Math.floor(raw[i] / sumRaw * (totalWidth - sumRaw));
23032
+ for (let i = 0; i < colCnt; i++) widths[i] = raw[i] + Math.floor(raw[i] / sumRaw * (totalWidth - sumRaw));
23033
+ free.clear();
22910
23034
  } else {
22911
23035
  for (; ; ) {
22912
23036
  const sum2 = [...free].reduce((a, i) => a + raw[i], 0);
22913
- const short = [...free].filter((i) => raw[i] / sum2 * budget < minW);
23037
+ const short = [...free].filter((i) => raw[i] / sum2 * budget < floor[i]);
22914
23038
  if (short.length === 0) break;
23039
+ const shortSum = short.reduce((a, i) => a + floor[i], 0);
23040
+ const scale = Math.min(1, (budget - (free.size - short.length) * minW) / shortSum);
22915
23041
  for (const i of short) {
22916
- widths[i] = minW;
23042
+ widths[i] = Math.max(minW, Math.floor(floor[i] * scale));
22917
23043
  free.delete(i);
22918
- budget -= minW;
23044
+ budget -= widths[i];
22919
23045
  }
22920
23046
  if (free.size === 0) break;
22921
23047
  }
@@ -22948,12 +23074,15 @@ function generateTable(rows, theme, style = null, remap = null, seq = 0) {
22948
23074
  const prof = takeProfile(remap, rowCnt, colCnt, anchorOfMarkdownCell(rows[0]?.[0] ?? ""), seq);
22949
23075
  const colMax = Array(colCnt).fill(0);
22950
23076
  const colMaxBody = Array(colCnt).fill(0);
23077
+ const colMinWord = Array(colCnt).fill(0);
22951
23078
  rows.forEach((row, r) => row.forEach((cell2, c) => {
22952
23079
  const w = cellContentWidth(cell2, measureH);
22953
23080
  if (w > colMax[c]) colMax[c] = w;
22954
23081
  if (r > 0 && w > colMaxBody[c]) colMaxBody[c] = w;
23082
+ const mw = cellMinWordWidth(cell2, measureH);
23083
+ if (mw > colMinWord[c]) colMinWord[c] = mw;
22955
23084
  }));
22956
- const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalW);
23085
+ const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalW, colMinWord);
22957
23086
  const colCentered = colWidths.map((w, c) => colMaxBody[c] + CELL_PAD <= w);
22958
23087
  const labelCol0 = !!reg && colCnt === 2 && colCentered[0] && rows.every((r) => (r[0] ?? "").replace(/\*\*|__|`/g, "").length <= 12);
22959
23088
  const tblId = nextTableId();
@@ -23032,16 +23161,19 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
23032
23161
  const prof = takeProfile(remap, rowCnt, colCnt, first ? anchorOfHtmlCell(first.inner) : "", seq);
23033
23162
  const colMax = Array(colCnt).fill(0);
23034
23163
  const colMaxBody = Array(colCnt).fill(0);
23164
+ const colMinWord = Array(colCnt).fill(0);
23035
23165
  const cellLines = placed.map((cell2) => htmlCellInnerToLines(cell2.inner).lines);
23036
23166
  placed.forEach((cell2, i) => {
23037
23167
  const w = Math.max(...cellLines[i].map((l) => measureTextWidth(unescapeHtml(l).trim(), measureH, 100)), 0) / cell2.colSpan;
23168
+ const mw = cell2.colSpan === 1 ? Math.max(...cellLines[i].map((l) => cellMinWordWidth(unescapeHtml(l), measureH)), 0) : 0;
23038
23169
  for (let dc = 0; dc < cell2.colSpan; dc++) {
23039
23170
  const c = cell2.c + dc;
23040
23171
  if (w > colMax[c]) colMax[c] = w;
23041
23172
  if (!cell2.isHeader && w > colMaxBody[c]) colMaxBody[c] = w;
23173
+ if (dc === 0 && mw > colMinWord[c]) colMinWord[c] = mw;
23042
23174
  }
23043
23175
  });
23044
- const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalWidth);
23176
+ const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalWidth, colMinWord);
23045
23177
  const colCentered = colWidths.map((w, c) => colMaxBody[c] + CELL_PAD <= w);
23046
23178
  const cellH = style ? Math.round(measureH * 1.6) + 282 : 1500;
23047
23179
  const tblW = colWidths.reduce((a, b) => a + b, 0);
@@ -23051,6 +23183,7 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
23051
23183
  const reg = style?.bfRegistry ?? null;
23052
23184
  let htmlHeaderRows = 0;
23053
23185
  while (htmlHeaderRows < rows.length && rows[htmlHeaderRows].tag === "th") htmlHeaderRows++;
23186
+ const tableRowHeights = Array(rowCnt).fill(cellH);
23054
23187
  const tcXmls = placed.map((cell2, i) => {
23055
23188
  const k = `${cell2.r},${cell2.c}`;
23056
23189
  const isHeader = cell2.isHeader;
@@ -23078,6 +23211,10 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
23078
23211
  }
23079
23212
  const contentH = Math.max(cellH * cell2.rowSpan, Math.max(lines.length, 1) * (style ? Math.round(measureH * 1.6) : 800) + nestedH);
23080
23213
  const cellHeight = Math.max(prof?.cellH.get(k) ?? 0, contentH);
23214
+ const perRow = Math.ceil(cellHeight / cell2.rowSpan);
23215
+ for (let r = cell2.r; r < Math.min(cell2.r + cell2.rowSpan, rowCnt); r++) {
23216
+ tableRowHeights[r] = Math.max(tableRowHeights[r], perRow);
23217
+ }
23081
23218
  const bf = prof?.cellBf.get(k) ?? (reg ? reg.get(dataCellSpec({
23082
23219
  row: cell2.r,
23083
23220
  rowEnd: cell2.r + cell2.rowSpan - 1,
@@ -23095,7 +23232,8 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
23095
23232
  const rowTcs = tcXmls.filter((_, i) => placed[i].r === r);
23096
23233
  trXmls.push(`<hp:tr>${rowTcs.join("")}</hp:tr>`);
23097
23234
  }
23098
- return `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="${style ? 1 : 0}" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="2" noShading="0"><hp:sz width="${tblW}" widthRelTo="ABSOLUTE" height="${cellH * rowCnt}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:inMargin left="510" right="510" top="141" bottom="141"/>` + trXmls.join("") + `</hp:tbl>`;
23235
+ const tableH = tableRowHeights.reduce((sum, height) => sum + height, 0);
23236
+ return `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="${style ? 1 : 0}" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="2" noShading="0"><hp:sz width="${tblW}" widthRelTo="ABSOLUTE" height="${tableH}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:inMargin left="510" right="510" top="141" bottom="141"/>` + trXmls.join("") + `</hp:tbl>`;
23099
23237
  }
23100
23238
 
23101
23239
  // src/hwpx/gen-gongmun-extra.ts
@@ -23110,9 +23248,9 @@ function para(text, paraPrId, charPrId) {
23110
23248
  return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
23111
23249
  }
23112
23250
  var APPROVAL_COL_W = 7085;
23113
- var APPROVAL_LABEL_H = 1100;
23251
+ var APPROVAL_LABEL_H = 1300;
23114
23252
  var APPROVAL_SIGN_H = 4200;
23115
- function buildApprovalTable(labels, gongmun, reg) {
23253
+ function buildApprovalTable(labels, reg, approvalCharPr) {
23116
23254
  const n = labels.length;
23117
23255
  const w = APPROVAL_COL_W * n;
23118
23256
  const bfAt = (row, col) => reg.get({
@@ -23122,10 +23260,10 @@ function buildApprovalTable(labels, gongmun, reg) {
23122
23260
  r: col === n - 1 ? "thick" : "thin"
23123
23261
  });
23124
23262
  const labelRow = labels.map(
23125
- (label, c) => tc({ bf: bfAt(0, c), row: 0, col: c, w: APPROVAL_COL_W, h: APPROVAL_LABEL_H, paras: para(label, GONGMUN_TBL_CENTER, CHAR_NORMAL) })
23263
+ (label, c) => tc({ bf: bfAt(0, c), row: 0, col: c, w: APPROVAL_COL_W, h: APPROVAL_LABEL_H, paras: para(label, GONGMUN_PARA_APPROVAL, approvalCharPr) })
23126
23264
  ).join("");
23127
23265
  const signRow = labels.map(
23128
- (_, c) => tc({ bf: bfAt(1, c), row: 1, col: c, w: APPROVAL_COL_W, h: APPROVAL_SIGN_H, paras: para("", GONGMUN_TBL_CENTER, CHAR_NORMAL) })
23266
+ (_, c) => tc({ bf: bfAt(1, c), row: 1, col: c, w: APPROVAL_COL_W, h: APPROVAL_SIGN_H, paras: para("", GONGMUN_PARA_APPROVAL, approvalCharPr) })
23129
23267
  ).join("");
23130
23268
  const table2 = tbl([labelRow, signRow], w, APPROVAL_LABEL_H + APPROVAL_SIGN_H, n);
23131
23269
  return `<hp:p paraPrIDRef="${GONGMUN_RIGHT}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
@@ -23137,13 +23275,13 @@ function buildEndMark() {
23137
23275
  return para(" \uB05D.", PARA_NORMAL, CHAR_NORMAL);
23138
23276
  }
23139
23277
  var TITLE_BOX = { barH: 382, titleH: 2850, color: "#0080C0", gradient: ["#0080C0", "#3CBFFF"] };
23140
- function buildTitleBox(title, titleCharPr, bodyWidth, reg) {
23278
+ function buildTitleBox(title, titleCharPr, barCharPr, bodyWidth, reg) {
23141
23279
  const w = bodyWidth - 280;
23142
23280
  const outer = { t: "thin", b: "thin", l: "thin", r: "thin" };
23143
23281
  const barTop = reg.get({ ...outer, fill: TITLE_BOX.color });
23144
23282
  const barBottom = reg.get({ ...outer, fill: { gradient: TITLE_BOX.gradient } });
23145
23283
  const mid = reg.get(outer);
23146
- const barPara = `<hp:p paraPrIDRef="${GONGMUN_TBL_CENTER}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t></hp:t></hp:run></hp:p>`;
23284
+ const barPara = para("", GJ_PARA_BAR, barCharPr);
23147
23285
  const rows = [
23148
23286
  tc({ bf: barTop, row: 0, col: 0, w, h: TITLE_BOX.barH, paras: barPara }),
23149
23287
  tc({ bf: mid, row: 1, col: 0, w, h: TITLE_BOX.titleH, paras: para(title, GONGMUN_TBL_CENTER, titleCharPr) }),
@@ -23153,6 +23291,118 @@ function buildTitleBox(title, titleCharPr, bodyWidth, reg) {
23153
23291
  return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
23154
23292
  }
23155
23293
 
23294
+ // src/hwpx/gen-docframe.ts
23295
+ function docframeIds(base) {
23296
+ return { org: base, sender: base + 1, foot: base + 2, small: base + 3, pressLabel: base + 4, pressHead: base + 5, pressTitle: base + 6, pressSub: base + 7, pressContact: base + 8 };
23297
+ }
23298
+ function docframeActive(g) {
23299
+ return !!(g.docHead || g.docFoot || g.reportInfo || g.noticeHead || g.press);
23300
+ }
23301
+ function docframeCharPrXmls(base, richAssets) {
23302
+ const body = richAssets ? 4 : 0;
23303
+ const ids = docframeIds(base);
23304
+ return [
23305
+ charPr(ids.org, 1800, true, false, body),
23306
+ charPr(ids.sender, 2200, true, false, body),
23307
+ charPr(ids.foot, 900, false, false, body),
23308
+ charPr(ids.small, 1200, false, false, body),
23309
+ charPr(ids.pressLabel, 2e3, true, false, body),
23310
+ charPr(ids.pressHead, 1e3, true, false, body),
23311
+ charPr(ids.pressTitle, 2500, true, false, body),
23312
+ charPr(ids.pressSub, 1500, true, false, body),
23313
+ charPr(ids.pressContact, 1e3, false, false, body)
23314
+ ];
23315
+ }
23316
+ function labeled(label, value, paraPrId) {
23317
+ return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_BOLD}"><hp:t>${escapeXml(label)}</hp:t></hp:run><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t>${escapeXml(value)}</hp:t></hp:run></hp:p>`;
23318
+ }
23319
+ function blank() {
23320
+ return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t></hp:t></hp:run></hp:p>`;
23321
+ }
23322
+ function buildDocHead(g, ids) {
23323
+ const h = g.docHead;
23324
+ const out = [];
23325
+ if (h.org) {
23326
+ out.push(para(h.org, GONGMUN_CENTER, ids.org), blank());
23327
+ }
23328
+ if (h.to !== void 0) out.push(labeled("\uC218\uC2E0 ", h.to, PARA_NORMAL));
23329
+ out.push(para(h.via ? `(\uACBD\uC720) ${h.via}` : "(\uACBD\uC720)", PARA_NORMAL, CHAR_NORMAL));
23330
+ if (h.title !== void 0) out.push(labeled("\uC81C\uBAA9 ", h.title, PARA_NORMAL));
23331
+ out.push(blank());
23332
+ return out;
23333
+ }
23334
+ var FOOT_SEP = "\u2500".repeat(46);
23335
+ function buildDocFoot(g, ids) {
23336
+ const f = g.docFoot;
23337
+ const out = [blank()];
23338
+ if (f.sender) {
23339
+ out.push(para(f.sender, GONGMUN_CENTER, ids.sender), blank());
23340
+ }
23341
+ const foot = (text) => para(text, GONGMUN_TBL_LEFT, ids.foot);
23342
+ out.push(foot(FOOT_SEP));
23343
+ if (f.drafter || f.reviewer || f.approver)
23344
+ out.push(foot(`\uAE30\uC548\uC790 ${f.drafter ?? ""} \uAC80\uD1A0\uC790 ${f.reviewer ?? ""} \uACB0\uC7AC\uAD8C\uC790 ${f.approver ?? ""}`));
23345
+ if (f.cooperator) out.push(foot(`\uD611\uC870\uC790 ${f.cooperator}`));
23346
+ if (f.docNum || f.receive) out.push(foot(`\uC2DC\uD589 ${f.docNum ?? ""} \uC811\uC218 ${f.receive ?? ""}`));
23347
+ if (f.address || f.site) out.push(foot(`${f.address ?? ""} / ${f.site ?? ""}`));
23348
+ if (f.phone || f.fax || f.email || f.disclosure)
23349
+ out.push(foot(`\uC804\uD654 ${f.phone ?? ""} \uC804\uC1A1 ${f.fax ?? ""} / ${f.email ?? ""} / ${f.disclosure ?? ""}`));
23350
+ return out;
23351
+ }
23352
+ function buildReportInfo(g, ids) {
23353
+ return para(g.reportInfo, GONGMUN_RIGHT, ids.small);
23354
+ }
23355
+ function buildNoticeHead(g) {
23356
+ const n = g.noticeHead;
23357
+ return n.no ? [para(n.no, PARA_NORMAL, CHAR_BOLD), blank()] : [];
23358
+ }
23359
+ function buildNoticeFoot(g) {
23360
+ const n = g.noticeHead;
23361
+ const out = [];
23362
+ if (n.date) out.push(blank(), para(n.date, GONGMUN_RIGHT, CHAR_NORMAL));
23363
+ if (n.sender) out.push(blank(), para(n.sender, GONGMUN_RIGHT, CHAR_BOLD));
23364
+ return out;
23365
+ }
23366
+ var PRESS_LABEL_H = 3200;
23367
+ var PRESS_HEAD_H = 900;
23368
+ function buildPressHead(g, ids, bodyWidth, reg) {
23369
+ const p = g.press;
23370
+ const w = bodyWidth - 280;
23371
+ const thick = { t: "thick", b: "thick", l: "thick", r: "thick" };
23372
+ const rows = [tc({ bf: reg.get({ ...thick, b: "thin" }), row: 0, col: 0, w, h: PRESS_LABEL_H, paras: para("\uBCF4\uB3C4\uC790\uB8CC", GONGMUN_TBL_CENTER, ids.pressLabel) })];
23373
+ const headLine = [
23374
+ p.release ? `\uBCF4\uB3C4\uC2DC\uC810 : ${p.release}` : "",
23375
+ p.distribute ? `\uBC30\uD3EC : ${p.distribute}` : ""
23376
+ ].filter(Boolean).join(" / ");
23377
+ if (headLine) rows.push(tc({ bf: reg.get({ ...thick, t: "thin" }), row: 1, col: 0, w, h: PRESS_HEAD_H, paras: para(headLine, GONGMUN_TBL_CENTER, ids.pressHead) }));
23378
+ const table2 = tbl(rows, w, PRESS_LABEL_H + (headLine ? PRESS_HEAD_H : 0), 1);
23379
+ return [`<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`, blank()];
23380
+ }
23381
+ function buildPressContact(g, ids, bodyWidth, reg) {
23382
+ const c = g.press?.contact;
23383
+ if (!c || !c.dept && !c.manager && !c.phone) return [];
23384
+ const w = bodyWidth - 280;
23385
+ const cells = [["\uB2F4\uB2F9 \uBD80\uC11C", c.dept ?? ""], ["\uB2F4\uB2F9\uC790", c.manager ?? ""], ["\uC5F0\uB77D\uCC98", c.phone ?? ""]];
23386
+ const labelW = Math.round(w * 0.14);
23387
+ const valueW = Math.round(w / 3) - labelW;
23388
+ let col = 0;
23389
+ const row = cells.flatMap(([label, value], i) => {
23390
+ const bf = (first) => reg.get({
23391
+ t: "thick",
23392
+ b: "thick",
23393
+ l: first && i === 0 ? "thick" : "thin",
23394
+ r: !first && i === cells.length - 1 ? "thick" : "thin",
23395
+ fill: first ? "#E6E6E6" : void 0
23396
+ });
23397
+ return [
23398
+ tc({ bf: bf(true), row: 0, col: col++, w: labelW, h: 1100, paras: para(label, GONGMUN_TBL_CENTER, ids.pressHead) }),
23399
+ tc({ bf: bf(false), row: 0, col: col++, w: valueW, h: 1100, paras: para(value, GONGMUN_TBL_CENTER, ids.pressContact) })
23400
+ ];
23401
+ });
23402
+ const table2 = tbl([row.join("")], w, 1100, cells.length * 2);
23403
+ return [blank(), `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`];
23404
+ }
23405
+
23156
23406
  // src/hwpx/equation-generate.ts
23157
23407
  var MAX_EQUATION_SOURCE = 1e4;
23158
23408
  var MAX_GROUP_DEPTH = 64;
@@ -23445,9 +23695,10 @@ function generateSecPr(gongmun) {
23445
23695
  m.footer = gongmun.headerFooter;
23446
23696
  }
23447
23697
  const secPr = `<hp:secPr textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="1" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY"><hp:margin header="${m.header}" footer="${m.footer}" gutter="0" left="${m.left}" right="${m.right}" top="${m.top}" bottom="${m.bottom}"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr></hp:secPr>`;
23448
- return gongmun?.pageNumbers ? secPr + pageNumCtrl() : secPr;
23698
+ const colPr = `<hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
23699
+ return secPr + colPr + (gongmun?.pageNumbers ? pageNumCtrl() : "");
23449
23700
  }
23450
- function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null) {
23701
+ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null, dfIds = null) {
23451
23702
  const paraXmls = [];
23452
23703
  let isFirst = true;
23453
23704
  let tableSeq = 0;
@@ -23455,13 +23706,16 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23455
23706
  let prevWasOrdered = false;
23456
23707
  const gaejosik = gongmun?.preset === "gaejosik";
23457
23708
  const measured = !!gongmun && usesReportFonts(gongmun.preset);
23458
- const vBase = charVariantBase(measured);
23709
+ const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
23710
+ const vBase = charVariantBase(richAssets, !!gongmun);
23459
23711
  const tableStyle = gongmun ? {
23460
23712
  totalWidth: mmToHwpunit(210 - gongmun.margins.left - gongmun.margins.right),
23461
- charPr: measured ? GJ_CHAR_TABLE : CHAR_NORMAL,
23462
- boldCharPr: measured ? GJ_CHAR_TABLE_BOLD : CHAR_BOLD,
23463
- charHeight: measured ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : gongmun.bodyHeight,
23464
- headerBf: gongmunTableHeaderBf(gaejosik),
23713
+ // 크기 실측: 실측 프리셋 맑은 고딕 12pt(22·23), 비실측도 12pt(11·12,
23714
+ // 실결재 지배값 본문 크기 셀은 열폭 부족으로 서술 열 세로 신장, v4.0.2 QA)
23715
+ charPr: richAssets ? GJ_CHAR_TABLE : GONGMUN_TBL_CHAR,
23716
+ boldCharPr: richAssets ? GJ_CHAR_TABLE_BOLD : GONGMUN_TBL_CHAR_BOLD,
23717
+ charHeight: richAssets ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : GONGMUN_TBL_PT,
23718
+ headerBf: gongmunTableHeaderBf(richAssets),
23465
23719
  centerParaPr: GONGMUN_CENTER,
23466
23720
  tblCenterParaPr: GONGMUN_TBL_CENTER,
23467
23721
  tblLeftParaPr: GONGMUN_TBL_LEFT,
@@ -23475,12 +23729,25 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23475
23729
  let pendingPageBreak = false;
23476
23730
  let pendingNewNum = false;
23477
23731
  let titleBoxH1Idx = -1;
23732
+ let pressH1Idx = -1;
23478
23733
  let hasFrontPages = false;
23479
23734
  const preamble = [];
23735
+ if (gongmun?.reportInfo && dfIds) {
23736
+ preamble.push(buildReportInfo(gongmun, dfIds));
23737
+ }
23480
23738
  if (gongmun?.approval && bfReg) {
23481
- preamble.push(buildApprovalTable(gongmun.approval, gongmun, bfReg));
23739
+ preamble.push(buildApprovalTable(gongmun.approval, bfReg, richAssets ? GJ_CHAR_APPROVAL : GONGMUN_APPROVAL_CHAR));
23740
+ }
23741
+ if (gongmun?.docHead && dfIds) {
23742
+ preamble.push(...buildDocHead(gongmun, dfIds));
23482
23743
  }
23483
- if (gaejosik && gongmun) {
23744
+ if (gongmun?.noticeHead && dfIds) {
23745
+ preamble.push(...buildNoticeHead(gongmun));
23746
+ }
23747
+ if (gongmun?.press && dfIds && bfReg && tableStyle) {
23748
+ preamble.push(...buildPressHead(gongmun, dfIds, tableStyle.totalWidth, bfReg));
23749
+ }
23750
+ if (gongmun && (gongmun.cover || gongmun.toc)) {
23484
23751
  const h1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
23485
23752
  const chapters = blocks.filter((b) => b.type === "heading" && b.level === 2).map((b) => stripChapterNumber(b.text || ""));
23486
23753
  let coverTitle = "";
@@ -23502,8 +23769,11 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23502
23769
  if (gongmun.bodyTitleBox && coverTitle && hasFrontPages) {
23503
23770
  preamble.push(buildGaejosikBodyTitle(coverTitle, gongmun).replace(/^<hp:p /, `<hp:p pageBreak="1" `));
23504
23771
  }
23505
- } else if (gongmun && !gaejosik && bfReg && (gongmun.preset === "report" || gongmun.preset === "plan" || gongmun.preset === "notice")) {
23772
+ }
23773
+ if (gongmun && !gaejosik && coverH1Idx < 0 && bfReg && (gongmun.preset === "report" || gongmun.preset === "plan" || gongmun.preset === "notice")) {
23506
23774
  titleBoxH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
23775
+ } else if (gongmun?.press && coverH1Idx < 0 && dfIds) {
23776
+ pressH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
23507
23777
  }
23508
23778
  if (preamble.length > 0) {
23509
23779
  preamble[0] = preamble[0].replace(` pageBreak="1"`, "").replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${generateSecPr(gongmun)}`);
@@ -23523,13 +23793,27 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23523
23793
  }
23524
23794
  switch (block.type) {
23525
23795
  case "heading": {
23796
+ if (gongmun && blockIdx === coverH1Idx) break;
23797
+ if (gongmun && blockIdx === pressH1Idx && dfIds) {
23798
+ const parts = [generateParagraph((block.text || "").trim(), GONGMUN_CENTER, dfIds.pressTitle)];
23799
+ for (const s of gongmun.press?.sub ?? []) {
23800
+ parts.push(generateParagraph(`- ${s} -`, GONGMUN_CENTER, dfIds.pressSub));
23801
+ }
23802
+ xml = parts.join("\n ");
23803
+ break;
23804
+ }
23526
23805
  if (gongmun && blockIdx === titleBoxH1Idx && tableStyle && bfReg) {
23527
- xml = buildTitleBox((block.text || "").trim(), measured ? GJ_CHAR_BODY_TITLE : CHAR_H1, tableStyle.totalWidth, bfReg);
23806
+ xml = buildTitleBox(
23807
+ (block.text || "").trim(),
23808
+ measured ? GJ_CHAR_BODY_TITLE : CHAR_H1,
23809
+ richAssets ? GJ_CHAR_TITLE_BAR : GONGMUN_TITLE_BAR_CHAR,
23810
+ tableStyle.totalWidth,
23811
+ bfReg
23812
+ );
23528
23813
  break;
23529
23814
  }
23530
23815
  if (gaejosik) {
23531
23816
  const lvl = block.level || 1;
23532
- if (blockIdx === coverH1Idx) break;
23533
23817
  if (lvl <= 2) {
23534
23818
  chapterNo++;
23535
23819
  xml = buildGaejosikChapter(chapterNo, stripChapterNumber(block.text || ""), gongmun);
@@ -23541,7 +23825,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23541
23825
  (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id
23542
23826
  );
23543
23827
  } else {
23544
- xml = generateParagraph(`\u25CB ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
23828
+ xml = generateParagraph(`${gongmun.bullet2} ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
23545
23829
  }
23546
23830
  break;
23547
23831
  }
@@ -23557,7 +23841,8 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23557
23841
  break;
23558
23842
  }
23559
23843
  case "paragraph": {
23560
- if (measured && (block.text || "").trimStart().startsWith("\u203B")) {
23844
+ const pTrim = (block.text || "").trimStart();
23845
+ if (measured && (pTrim.startsWith("\u203B") || /^\*\s/.test(pTrim))) {
23561
23846
  xml = generateParagraph((block.text || "").trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
23562
23847
  break;
23563
23848
  }
@@ -23624,14 +23909,26 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23624
23909
  xml = generateParagraph(content.trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
23625
23910
  break;
23626
23911
  }
23912
+ if (measured && block.marker === "*") {
23913
+ xml = generateParagraph(`* ${content.trim()}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
23914
+ break;
23915
+ }
23627
23916
  const text = marker2 ? `${marker2} ${content}` : content;
23917
+ const listParaPr = marker2 ? GONGMUN_LIST_BASE + depth : GONGMUN_LIST_PLAIN_BASE + depth;
23628
23918
  let listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
23629
23919
  let mapId = fit ? variantMapper(fit, blockIdx, vBase) : void 0;
23630
23920
  if (measured && depth === 0 && gongmun.numbering !== "standard") {
23631
23921
  listCharPr = GJ_CHAR_DAE;
23632
23922
  mapId = (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id;
23633
23923
  }
23634
- xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr, mapId);
23924
+ if (gongmun.preset === "press") {
23925
+ if (depth === 0) listCharPr = CHAR_NORMAL;
23926
+ if (depth >= 2 && dfIds) {
23927
+ listCharPr = dfIds.small;
23928
+ mapId = void 0;
23929
+ }
23930
+ }
23931
+ xml = generateParagraph(text, listParaPr, listCharPr, mapId);
23635
23932
  break;
23636
23933
  }
23637
23934
  const indent = block.indent || 0;
@@ -23705,10 +24002,19 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23705
24002
  }
23706
24003
  paraXmls.push(xml);
23707
24004
  }
24005
+ if (gongmun?.noticeHead && dfIds) {
24006
+ paraXmls.push(...buildNoticeFoot(gongmun));
24007
+ }
23708
24008
  if (gongmun?.endMark && paraXmls.length > 0) {
23709
24009
  const lastText = [...blocks].reverse().find((b) => (b.type === "paragraph" || b.type === "list_item") && (b.text || "").trim())?.text ?? "";
23710
24010
  if (!hasEndMark(lastText)) paraXmls.push(buildEndMark());
23711
24011
  }
24012
+ if (gongmun?.docFoot && dfIds) {
24013
+ paraXmls.push(...buildDocFoot(gongmun, dfIds));
24014
+ }
24015
+ if (gongmun?.press && dfIds && bfReg && tableStyle) {
24016
+ paraXmls.push(...buildPressContact(gongmun, dfIds, tableStyle.totalWidth, bfReg));
24017
+ }
23712
24018
  if (paraXmls.length === 0) {
23713
24019
  paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run></hp:p>`);
23714
24020
  }
@@ -23722,16 +24028,19 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23722
24028
  async function markdownToHwpx(markdown, options) {
23723
24029
  const theme = resolveTheme(options?.theme);
23724
24030
  const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
23725
- const gaejosik = gongmun?.preset === "gaejosik";
23726
24031
  const measured = !!gongmun && usesReportFonts(gongmun.preset);
24032
+ const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
23727
24033
  const blocks = parseMarkdownToBlocks(markdown);
23728
24034
  const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
23729
24035
  const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
23730
- const staticBfEnd = gongmun ? gaejosik ? 11 : 4 : 3;
23731
- const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(measured) + (fit?.variants?.length ?? 0) * 4, staticBfEnd) : null;
24036
+ const staticBfEnd = gongmun ? richAssets ? 11 : 4 : 3;
24037
+ const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4, staticBfEnd) : null;
23732
24038
  const bfReg = gongmun ? new TableBfRegistry(staticBfEnd + (remap?.borderFillXmls.length ?? 0)) : null;
24039
+ const dfActive = !!gongmun && docframeActive(gongmun);
24040
+ const dfBase = charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4 + (remap?.charPrXmls.length ?? 0);
24041
+ const dfIds = dfActive ? docframeIds(dfBase) : null;
23733
24042
  const chartParts = [];
23734
- const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap);
24043
+ const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap, dfIds);
23735
24044
  if (remap && remap.tables.length > 0) {
23736
24045
  const unused = remap.tables.filter((t) => !t.used).length;
23737
24046
  if (unused === remap.tables.length) {
@@ -23743,7 +24052,13 @@ async function markdownToHwpx(markdown, options) {
23743
24052
  zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
23744
24053
  zip.file("META-INF/container.xml", generateContainerXml());
23745
24054
  zip.file("Contents/content.hpf", generateManifest(chartParts));
23746
- zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? [], extraBorderFills, remap?.charPrXmls ?? []));
24055
+ zip.file("Contents/header.xml", generateHeaderXml(
24056
+ theme,
24057
+ gongmun,
24058
+ fit?.variants ?? [],
24059
+ extraBorderFills,
24060
+ [...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase, richAssets) : []]
24061
+ ));
23747
24062
  zip.file("Contents/section0.xml", sectionXml);
23748
24063
  for (const part of chartParts) zip.file(part.name, part.xml);
23749
24064
  zip.file("Preview/PrvText.txt", buildPrvText(blocks));
@@ -28551,7 +28866,7 @@ async function parseHwp(buffer, options) {
28551
28866
  async function parsePdf(buffer, options) {
28552
28867
  let parsePdfDocument;
28553
28868
  try {
28554
- const mod = await import("./parser-G433UZMX.js");
28869
+ const mod = await import("./parser-J7YREDNS.js");
28555
28870
  parsePdfDocument = mod.parsePdfDocument;
28556
28871
  } catch {
28557
28872
  return {