kordoc 4.0.0 → 4.0.2

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 (44) hide show
  1. package/dist/{-URVKBXYK.js → -XMKVL3NX.js} +11 -7
  2. package/dist/{chunk-IA5UZZKV.js → chunk-3IYQYBUF.js} +2 -2
  3. package/dist/{chunk-THZVBYHE.js → chunk-67RANGFQ.js} +3 -3
  4. package/dist/{chunk-JUJEKFUF.js → chunk-6SD4X5PT.js} +3 -3
  5. package/dist/{chunk-7ETIIYGI.js → chunk-6YIYFWO2.js} +2 -2
  6. package/dist/{chunk-PGWAF755.js → chunk-AHWRAGXN.js} +477 -89
  7. package/dist/chunk-AHWRAGXN.js.map +1 -0
  8. package/dist/{chunk-DVUNWK6W.js → chunk-IKBIIEVJ.js} +2 -2
  9. package/dist/{chunk-ZKG6SBHV.js → chunk-STMLZDKR.js} +2 -2
  10. package/dist/{chunk-EBSCV3JC.cjs → chunk-WN7LZHII.cjs} +2 -2
  11. package/dist/{chunk-EBSCV3JC.cjs.map → chunk-WN7LZHII.cjs.map} +1 -1
  12. package/dist/cli.js +72 -14
  13. package/dist/cli.js.map +1 -1
  14. package/dist/index.cjs +730 -342
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +93 -6
  17. package/dist/index.d.ts +93 -6
  18. package/dist/index.js +472 -84
  19. package/dist/index.js.map +1 -1
  20. package/dist/mcp.js +60 -14
  21. package/dist/mcp.js.map +1 -1
  22. package/dist/{parser-NXHY37WH.js → parser-2Q2YKEJG.js} +2 -2
  23. package/dist/{parser-LHUO6UUA.js → parser-3HM2VYKK.js} +3 -3
  24. package/dist/{parser-6TV6JKHF.cjs → parser-6AYYGX6J.cjs} +14 -14
  25. package/dist/{parser-6TV6JKHF.cjs.map → parser-6AYYGX6J.cjs.map} +1 -1
  26. package/dist/render-W4VCJE6N.js +10 -0
  27. package/dist/seal-DRPTNHOP.js +10 -0
  28. package/dist/{watch-PNXTAWDR.js → watch-6OHF727O.js} +7 -7
  29. package/package.json +1 -1
  30. package/dist/chunk-PGWAF755.js.map +0 -1
  31. package/dist/render-T5O6QHBJ.js +0 -10
  32. package/dist/seal-7KNR5WFH.js +0 -10
  33. /package/dist/{-URVKBXYK.js.map → -XMKVL3NX.js.map} +0 -0
  34. /package/dist/{chunk-IA5UZZKV.js.map → chunk-3IYQYBUF.js.map} +0 -0
  35. /package/dist/{chunk-THZVBYHE.js.map → chunk-67RANGFQ.js.map} +0 -0
  36. /package/dist/{chunk-JUJEKFUF.js.map → chunk-6SD4X5PT.js.map} +0 -0
  37. /package/dist/{chunk-7ETIIYGI.js.map → chunk-6YIYFWO2.js.map} +0 -0
  38. /package/dist/{chunk-DVUNWK6W.js.map → chunk-IKBIIEVJ.js.map} +0 -0
  39. /package/dist/{chunk-ZKG6SBHV.js.map → chunk-STMLZDKR.js.map} +0 -0
  40. /package/dist/{parser-NXHY37WH.js.map → parser-2Q2YKEJG.js.map} +0 -0
  41. /package/dist/{parser-LHUO6UUA.js.map → parser-3HM2VYKK.js.map} +0 -0
  42. /package/dist/{render-T5O6QHBJ.js.map → render-W4VCJE6N.js.map} +0 -0
  43. /package/dist/{seal-7KNR5WFH.js.map → seal-DRPTNHOP.js.map} +0 -0
  44. /package/dist/{watch-PNXTAWDR.js.map → watch-6OHF727O.js.map} +0 -0
@@ -11,7 +11,7 @@ import {
11
11
  dedupeRunningHeaders,
12
12
  flattenLayoutTables,
13
13
  mapPuaText
14
- } from "./chunk-7ETIIYGI.js";
14
+ } from "./chunk-6YIYFWO2.js";
15
15
  import {
16
16
  detectFormat,
17
17
  detectOle2Format,
@@ -32,14 +32,14 @@ import {
32
32
  paraTextPureT,
33
33
  patchZipEntries,
34
34
  scanSectionXml
35
- } from "./chunk-THZVBYHE.js";
35
+ } from "./chunk-67RANGFQ.js";
36
36
  import {
37
37
  SPACE_EM_FIXED,
38
38
  charWidthEm1000,
39
39
  fitRatioForFewerLines,
40
40
  measureTextWidth,
41
41
  simulateWrap
42
- } from "./chunk-JUJEKFUF.js";
42
+ } from "./chunk-6SD4X5PT.js";
43
43
  import {
44
44
  MAX_DECOMPRESS_SIZE,
45
45
  MAX_XML_DEPTH,
@@ -50,7 +50,7 @@ import {
50
50
  createXmlParser,
51
51
  extractTextFromNode,
52
52
  findChildByLocalName
53
- } from "./chunk-DVUNWK6W.js";
53
+ } from "./chunk-IKBIIEVJ.js";
54
54
  import {
55
55
  KordocError,
56
56
  classifyError,
@@ -61,7 +61,7 @@ import {
61
61
  sanitizeHref,
62
62
  stripDtd,
63
63
  toArrayBuffer
64
- } from "./chunk-ZKG6SBHV.js";
64
+ } from "./chunk-STMLZDKR.js";
65
65
 
66
66
  // src/index.ts
67
67
  import { readFile } from "fs/promises";
@@ -19746,8 +19746,9 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
19746
19746
  import JSZip6 from "jszip";
19747
19747
 
19748
19748
  // src/hwpx/gaejosik.ts
19749
- var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "\u2015", "\u318D"];
19750
- function gaejosikMarker(depth) {
19749
+ var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
19750
+ function gaejosikMarker(depth, bullet2 = "\u25CB") {
19751
+ if (depth === 1) return bullet2;
19751
19752
  return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
19752
19753
  }
19753
19754
  function gaejosikSizes(bodyHeight, ov = {}) {
@@ -19783,11 +19784,11 @@ function gaejosikSpaceBefore(depth, bodyHeight) {
19783
19784
  const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
19784
19785
  return Math.round(ui * s);
19785
19786
  }
19786
- function gaejosikLevelIndent(depth, bodyHeight, sizes = {}) {
19787
+ function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
19787
19788
  const lefts = [0, 1, 1.5, 2];
19788
19789
  const left = depth <= 3 ? Math.round(lefts[depth] * bodyHeight) : Math.round((2 + (depth - 3) * 0.5) * bodyHeight);
19789
19790
  const h = depth === 0 ? gaejosikSizes(bodyHeight, sizes).dae : bodyHeight;
19790
- return { left, indent: -markerWidth(gaejosikMarker(depth), h) };
19791
+ return { left, indent: -markerWidth(gaejosikMarker(depth, bullet2), h) };
19791
19792
  }
19792
19793
  function gaejosikChamIndent(bodyHeight, sizes = {}) {
19793
19794
  const cham = gaejosikSizes(bodyHeight, sizes).cham;
@@ -19855,7 +19856,7 @@ function bodyTitleGeom(bodyHeight, ov = {}) {
19855
19856
  }
19856
19857
 
19857
19858
  // src/hwpx/gongmun.ts
19858
- var OFFICIAL_MARGINS = { top: 20, bottom: 10, left: 20, right: 20 };
19859
+ var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
19859
19860
  var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
19860
19861
  var GAEJOSIK_HEADER_FOOTER = 4251;
19861
19862
  var PRESET_DEFAULTS = {
@@ -19864,7 +19865,9 @@ var PRESET_DEFAULTS = {
19864
19865
  plan: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
19865
19866
  notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
19866
19867
  minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
19867
- gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" }
19868
+ gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
19869
+ // 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
19870
+ press: { bodyPt: 14, lineSpacing: 160, numbering: "report" }
19868
19871
  };
19869
19872
  var PRESET_ALIAS = {
19870
19873
  official: "official",
@@ -19887,12 +19890,17 @@ var PRESET_ALIAS = {
19887
19890
  \uAC1C\uC870\uC2DD: "gaejosik",
19888
19891
  \uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
19889
19892
  \uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
19890
- \uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik"
19893
+ \uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
19894
+ press: "press",
19895
+ \uBCF4\uB3C4\uC790\uB8CC: "press"
19891
19896
  };
19892
19897
  function normalizeGongmunPreset(preset) {
19893
19898
  if (!preset) return "official";
19894
19899
  return PRESET_ALIAS[preset.trim()] ?? "official";
19895
19900
  }
19901
+ function usesReportFonts(preset) {
19902
+ return preset === "gaejosik" || preset === "report" || preset === "plan";
19903
+ }
19896
19904
  function resolveGongmun(opts) {
19897
19905
  const preset = normalizeGongmunPreset(opts.preset);
19898
19906
  const d = PRESET_DEFAULTS[preset];
@@ -19901,13 +19909,14 @@ function resolveGongmun(opts) {
19901
19909
  const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik";
19902
19910
  const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
19903
19911
  const gaejosik = preset === "gaejosik";
19912
+ const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
19904
19913
  return {
19905
19914
  preset,
19906
19915
  bodyFont: opts.bodyFont ?? "myeongjo",
19907
19916
  bodyHeight: Math.round(bodyPt * 100),
19908
19917
  lineSpacing: opts.lineSpacing ?? d.lineSpacing,
19909
19918
  numbering: opts.numbering ?? d.numbering,
19910
- margins: opts.margins ?? (gaejosik ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
19919
+ margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
19911
19920
  centerTitle: opts.centerTitle ?? true,
19912
19921
  autoFitMinRatio,
19913
19922
  cover: coverOn ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
@@ -19916,12 +19925,26 @@ function resolveGongmun(opts) {
19916
19925
  sizes: opts.sizes ?? {},
19917
19926
  // 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
19918
19927
  pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
19919
- headerFooter: gaejosik ? GAEJOSIK_HEADER_FOOTER : 0,
19928
+ // 머리말·꼬리말 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
19929
+ // 기안문 0(실결재 41/60건 h0/f0)
19930
+ headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
19920
19931
  // "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
19921
19932
  endMark: opts.endMark ?? preset === "official",
19922
19933
  approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
19923
19934
  // 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
19924
- bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn)
19935
+ bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
19936
+ // h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
19937
+ h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : preset === "notice" ? "number" : "none"),
19938
+ // 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
19939
+ bullet2: opts.bullet2 ?? (preset === "notice" || preset === "press" ? "\u3147" : "\u25CB"),
19940
+ // 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
19941
+ suppressSingle: opts.suppressSingle ?? false,
19942
+ docHead: preset === "official" && opts.docHead ? opts.docHead : null,
19943
+ docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
19944
+ reportInfo: opts.reportInfo?.trim() || null,
19945
+ noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
19946
+ // 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
19947
+ press: preset === "press" ? opts.press ?? {} : null
19925
19948
  };
19926
19949
  }
19927
19950
  var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
@@ -19939,6 +19962,7 @@ function circledHangul(n) {
19939
19962
  return String.fromCodePoint(12910 + n % 14);
19940
19963
  }
19941
19964
  var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
19965
+ var PRESS_BULLETS = ["\u25A1", "\u3147", "*", "\u318D"];
19942
19966
  function standardMarker(depth, n) {
19943
19967
  switch (depth) {
19944
19968
  case 0:
@@ -19961,17 +19985,19 @@ function standardMarker(depth, n) {
19961
19985
  return circledHangul(n);
19962
19986
  }
19963
19987
  }
19964
- function reportMarker(depth) {
19965
- return REPORT_BULLETS[Math.min(depth, REPORT_BULLETS.length - 1)];
19988
+ function reportMarker(depth, bullet2 = "\u25CB", press = false) {
19989
+ const bullets = press ? PRESS_BULLETS : REPORT_BULLETS;
19990
+ const m = bullets[Math.min(depth, bullets.length - 1)];
19991
+ return depth === 1 && !press ? bullet2 : m;
19966
19992
  }
19967
19993
  function markerWidth(marker, bodyHeight) {
19968
19994
  let em = SPACE_EM_FIXED;
19969
19995
  for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
19970
19996
  return Math.round(em / 1e3 * bodyHeight);
19971
19997
  }
19972
- function levelIndent(depth, bodyHeight, numbering, sizes = {}) {
19973
- if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes);
19974
- const marker = numbering === "report" ? reportMarker(depth) : standardMarker(depth, 0);
19998
+ function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", press = false) {
19999
+ if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
20000
+ const marker = numbering === "report" ? reportMarker(depth, bullet2, press) : standardMarker(depth, 0);
19975
20001
  return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, bodyHeight) };
19976
20002
  }
19977
20003
  function computeSuppression(depths) {
@@ -19988,8 +20014,10 @@ function computeSuppression(depths) {
19988
20014
  return keys.map((k) => (counts.get(k) ?? 0) <= 1);
19989
20015
  }
19990
20016
  var GongmunNumberer = class {
19991
- constructor(numbering) {
20017
+ constructor(numbering, bullet2 = "\u25CB", press = false) {
19992
20018
  this.numbering = numbering;
20019
+ this.bullet2 = bullet2;
20020
+ this.press = press;
19993
20021
  }
19994
20022
  counts = [];
19995
20023
  /** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
@@ -19998,8 +20026,8 @@ var GongmunNumberer = class {
19998
20026
  const n = this.counts[depth] ?? 0;
19999
20027
  this.counts[depth] = n + 1;
20000
20028
  if (suppress) return "";
20001
- if (this.numbering === "gaejosik") return gaejosikMarker(depth);
20002
- return this.numbering === "report" ? reportMarker(depth) : standardMarker(depth, n);
20029
+ if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
20030
+ return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.press) : standardMarker(depth, n);
20003
20031
  }
20004
20032
  reset() {
20005
20033
  this.counts = [];
@@ -20066,10 +20094,10 @@ function headingCharPrId(level) {
20066
20094
  if (level === 3) return CHAR_H3;
20067
20095
  return CHAR_H4;
20068
20096
  }
20069
- function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_COLOR, ratioPct = 100, keepFontOnBold = false) {
20097
+ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_COLOR, ratioPct = 100) {
20070
20098
  const boldAttr = bold ? ` bold="1"` : "";
20071
20099
  const italicAttr = italic ? ` italic="1"` : "";
20072
- const effFont = bold && !keepFontOnBold ? 2 : fontId;
20100
+ const effFont = fontId;
20073
20101
  const boldEl = bold ? `<hh:bold/>` : "";
20074
20102
  return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
20075
20103
  <hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
@@ -20121,6 +20149,7 @@ var GJ_PARA_COVER = 21;
20121
20149
  var GJ_PARA_TOC_ITEM = 22;
20122
20150
  var GJ_PARA_CHAPTER = 23;
20123
20151
  var GJ_PARA_BAR = 24;
20152
+ var GONGMUN_LIST_PLAIN_BASE = 25;
20124
20153
  var GJ_BF_CHAPTER_NUM = 3;
20125
20154
  var GJ_BF_CHAPTER_GAP = 4;
20126
20155
  var GJ_BF_CHAPTER_TITLE = 5;
@@ -20159,9 +20188,13 @@ function newPageNumCtrl(num2 = 1) {
20159
20188
  function pageHidingCtrl(hideHeader = false) {
20160
20189
  return `<hp:ctrl><hp:pageHiding hideHeader="${hideHeader ? 1 : 0}" hideFooter="0" hideMasterPage="0" hideBorder="0" hideFill="0" hidePageNum="1"/></hp:ctrl>`;
20161
20190
  }
20191
+ var GONGMUN_TBL_CHAR = 11;
20192
+ var GONGMUN_TBL_CHAR_BOLD = 12;
20193
+ var GONGMUN_TBL_PT = 1200;
20162
20194
  var CHAR_VARIANT_BASE = 11;
20163
- function charVariantBase(gaejosik) {
20164
- return gaejosik ? CHAR_VARIANT_BASE + GJ_CHAR_COUNT : CHAR_VARIANT_BASE;
20195
+ function charVariantBase(measured, isGongmun = true) {
20196
+ if (!isGongmun) return CHAR_VARIANT_BASE;
20197
+ return measured ? 11 + GJ_CHAR_COUNT : 11 + 2;
20165
20198
  }
20166
20199
  var GONGMUN_BODY_RATIO = 95;
20167
20200
 
@@ -20409,7 +20442,7 @@ function generateManifest(chartParts = []) {
20409
20442
  </opf:package>`;
20410
20443
  }
20411
20444
  function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
20412
- const gaejosik = gongmun?.preset === "gaejosik";
20445
+ const measured = !!gongmun && usesReportFonts(gongmun.preset);
20413
20446
  let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
20414
20447
  if (gongmun) {
20415
20448
  body = gongmun.bodyHeight;
@@ -20419,7 +20452,9 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
20419
20452
  h3 = body;
20420
20453
  h4 = Math.max(body - 100, 1300);
20421
20454
  }
20422
- const bodyFont = gaejosik ? 4 : 0;
20455
+ const bodyFont = measured ? 4 : 0;
20456
+ const hFont = measured ? 3 : 1;
20457
+ const hBold = !measured;
20423
20458
  const bodyRatio = gongmun ? GONGMUN_BODY_RATIO : 100;
20424
20459
  const rows = [
20425
20460
  charPr(0, body, false, false, bodyFont, theme.body, bodyRatio),
@@ -20427,25 +20462,31 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
20427
20462
  charPr(2, body, false, true, bodyFont, theme.body, bodyRatio),
20428
20463
  charPr(3, body, true, true, bodyFont, theme.body, bodyRatio),
20429
20464
  charPr(4, code, false, false, 1),
20430
- charPr(5, h1, true, false, 1, theme.h1),
20431
- charPr(6, h2, true, false, 1, theme.h2),
20432
- charPr(7, h3, true, false, 1, theme.h3),
20433
- charPr(8, h4, true, false, 1, theme.h4),
20465
+ charPr(5, h1, hBold, false, hFont, theme.h1),
20466
+ charPr(6, h2, hBold, false, hFont, theme.h2),
20467
+ charPr(7, h3, hBold, false, hFont, theme.h3),
20468
+ charPr(8, h4, hBold, false, hFont, theme.h4),
20434
20469
  charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, bodyFont, theme.tableHeader),
20435
20470
  charPr(CHAR_QUOTE, body, false, true, bodyFont, theme.quote)
20436
20471
  ];
20437
- if (gaejosik) {
20472
+ if (gongmun && !measured) {
20473
+ rows.push(
20474
+ charPr(11, GONGMUN_TBL_PT, false, false, bodyFont, theme.body),
20475
+ charPr(12, GONGMUN_TBL_PT, true, false, bodyFont, theme.body)
20476
+ );
20477
+ }
20478
+ if (measured) {
20438
20479
  const sz = gaejosikSizes(body, gongmun.sizes);
20439
20480
  rows.push(
20440
20481
  charPr(11, sz.dae, false, false, 3),
20441
20482
  // □ HY헤드라인M
20442
- charPr(12, sz.dae, true, false, 3, "#000000", 100, true),
20483
+ charPr(12, sz.dae, true, false, 3),
20443
20484
  // □ bold
20444
20485
  charPr(13, sz.cham, false, false, 5),
20445
20486
  // ※ 한양중고딕
20446
- charPr(14, sz.cham, true, false, 5, "#000000", 100, true),
20487
+ charPr(14, sz.cham, true, false, 5),
20447
20488
  // ※ bold
20448
- charPr(15, sz.chapter, true, false, 4, "#FFFFFF", 100, true),
20489
+ charPr(15, sz.chapter, true, false, 4, "#FFFFFF"),
20449
20490
  // 장 로마숫자(흰)
20450
20491
  charPr(16, sz.chapter, false, false, 3),
20451
20492
  // 장 제목
@@ -20453,15 +20494,15 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
20453
20494
  // 표지 제목
20454
20495
  charPr(18, sz.coverSub, false, false, 3),
20455
20496
  // 표지 날짜·기관명
20456
- charPr(19, sz.tocLabel, true, false, 3, "#000000", 100, true),
20497
+ charPr(19, sz.tocLabel, true, false, 3),
20457
20498
  // 목 차
20458
- charPr(20, sz.tocRoman, true, false, 6, "#000000", 100, true),
20499
+ charPr(20, sz.tocRoman, true, false, 6),
20459
20500
  // 목차 로마숫자(한양신명조)
20460
20501
  charPr(21, sz.tocItem, false, false, 3),
20461
20502
  // 목차 항목
20462
20503
  charPr(22, sz.table, false, false, 7),
20463
20504
  // 표 셀(맑은 고딕 12pt)
20464
- charPr(23, sz.table, true, false, 7, "#000000", 100, true),
20505
+ charPr(23, sz.table, true, false, 7),
20465
20506
  // 표 셀 bold
20466
20507
  charPr(24, sz.bar, false, false, 4),
20467
20508
  // 표지 바 셀 빈 문단(6pt)
@@ -20500,10 +20541,11 @@ ${base2.join("\n")}
20500
20541
  }
20501
20542
  const ls = gongmun.lineSpacing;
20502
20543
  const titleAlign = gongmun.centerTitle ? "CENTER" : "LEFT";
20544
+ const h2Geom = gongmun.h2Marker !== "none" ? { spaceBefore: Math.round(gongmun.bodyHeight * 2), spaceAfter: 0, indent: -markerWidth(gongmun.h2Marker === "box" ? "\u25A1" : "1.", 1600) } : { spaceBefore: 600, spaceAfter: 150 };
20503
20545
  const base = [
20504
20546
  paraPr(0, { lineSpacing: ls, keepWord: true }),
20505
20547
  paraPr(1, { align: titleAlign, spaceBefore: 400, spaceAfter: 400, lineSpacing: ls, keepWord: true }),
20506
- paraPr(2, { align: "LEFT", spaceBefore: 600, spaceAfter: 150, lineSpacing: ls, keepWord: true }),
20548
+ paraPr(2, { align: "LEFT", ...h2Geom, lineSpacing: ls, keepWord: true }),
20507
20549
  paraPr(3, { align: "LEFT", spaceBefore: 400, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
20508
20550
  paraPr(4, { align: "LEFT", spaceBefore: 300, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
20509
20551
  paraPr(5, { align: "LEFT", lineSpacing: 130, indent: 400, keepWord: true }),
@@ -20511,16 +20553,16 @@ ${base2.join("\n")}
20511
20553
  paraPr(7, { align: "LEFT", lineSpacing: ls, indent: 600, keepWord: true })
20512
20554
  ];
20513
20555
  for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
20514
- const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes);
20515
- const sectionGap = gongmun.numbering === "gaejosik" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : gongmun.numbering === "report" && d === 0 ? Math.round(gongmun.bodyHeight * 0.5) : 0;
20516
- const keepNext = gongmun.numbering === "gaejosik" && d === 0;
20556
+ const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, gongmun.preset === "press");
20557
+ const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : 0;
20558
+ const keepNext = (gongmun.numbering === "gaejosik" || gongmun.numbering === "report") && d === 0;
20517
20559
  base.push(paraPr(GONGMUN_LIST_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent, spaceBefore: sectionGap, keepWord: true, keepWithNext: keepNext }));
20518
20560
  }
20519
20561
  base.push(paraPr(GONGMUN_CENTER, { align: "CENTER", lineSpacing: ls, keepWord: true }));
20520
20562
  base.push(paraPr(GONGMUN_RIGHT, { align: "RIGHT", lineSpacing: ls, keepWord: true }));
20521
20563
  base.push(paraPr(GONGMUN_TBL_CENTER, { align: "CENTER", lineSpacing: 130, keepWord: true }));
20522
20564
  base.push(paraPr(GONGMUN_TBL_LEFT, { align: "LEFT", lineSpacing: 130, keepWord: true }));
20523
- if (gongmun.preset === "gaejosik") {
20565
+ {
20524
20566
  const cham = gaejosikChamIndent(gongmun.bodyHeight, gongmun.sizes);
20525
20567
  const toc = gaejosikTocItemIndent(gongmun.bodyHeight, gongmun.sizes);
20526
20568
  base.push(
@@ -20533,6 +20575,11 @@ ${base2.join("\n")}
20533
20575
  paraPr(GJ_PARA_BAR, { align: "CENTER", lineSpacing: 70, keepWord: true })
20534
20576
  );
20535
20577
  }
20578
+ for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
20579
+ const { left } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, gongmun.preset === "press");
20580
+ const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : 0;
20581
+ base.push(paraPr(GONGMUN_LIST_PLAIN_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent: 0, spaceBefore: sectionGap, keepWord: true }));
20582
+ }
20536
20583
  return `<hh:paraProperties itemCnt="${base.length}">
20537
20584
  ${base.join("\n")}
20538
20585
  </hh:paraProperties>`;
@@ -20554,14 +20601,15 @@ function fontEntry(id, face, weight = 6) {
20554
20601
  </hh:font>`;
20555
20602
  }
20556
20603
  function buildFontFaces(gongmun, bodyFace) {
20557
- if (gongmun?.preset === "gaejosik") {
20604
+ if (gongmun && usesReportFonts(gongmun.preset)) {
20558
20605
  const ov = gongmun.fonts;
20606
+ const bodyDefault = gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870";
20559
20607
  const faces = [
20560
20608
  "\uD568\uCD08\uB86C\uBC14\uD0D5",
20561
20609
  "\uD568\uCD08\uB86C\uB3CB\uC6C0",
20562
20610
  "HY\uACAC\uACE0\uB515",
20563
20611
  ov.heading ?? "HY\uD5E4\uB4DC\uB77C\uC778M",
20564
- ov.body ?? "\uD734\uBA3C\uBA85\uC870",
20612
+ ov.body ?? bodyDefault,
20565
20613
  ov.ref ?? "\uD55C\uC591\uC911\uACE0\uB515",
20566
20614
  "\uD55C\uC591\uC2E0\uBA85\uC870",
20567
20615
  ov.table ?? "\uB9D1\uC740 \uACE0\uB515"
@@ -20911,7 +20959,9 @@ function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
20911
20959
  if (gongmun.numbering === "gaejosik" && (depth === 0 || (block.text || "").trimStart().startsWith("\u203B"))) continue;
20912
20960
  const content = plainRenderText(block.text || "");
20913
20961
  text = marker ? `${marker} ${content}` : content;
20914
- const { left, indent } = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering);
20962
+ const li = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, gongmun.preset === "press");
20963
+ const left = li.left;
20964
+ const indent = marker ? li.indent : 0;
20915
20965
  firstW = pageW - left - Math.max(indent, 0);
20916
20966
  contW = pageW - left - Math.max(-indent, 0);
20917
20967
  } else if (block.type === "paragraph") {
@@ -20939,7 +20989,7 @@ function variantMapper(fit, blockIdx, base = CHAR_VARIANT_BASE) {
20939
20989
  }
20940
20990
  function precomputeGongmunList(blocks, gongmun) {
20941
20991
  const result = /* @__PURE__ */ new Map();
20942
- const depthOffset = gongmun.numbering === "gaejosik" && blocks.some((b) => b.type === "heading" && b.level === 3) ? 1 : 0;
20992
+ 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;
20943
20993
  let i = 0;
20944
20994
  while (i < blocks.length) {
20945
20995
  if (blocks[i].type !== "list_item") {
@@ -20966,8 +21016,8 @@ function precomputeGongmunList(blocks, gongmun) {
20966
21016
  break;
20967
21017
  }
20968
21018
  const depths = run.map((bi) => Math.min(Math.max((blocks[bi].indent || 0) + depthOffset, 0), GONGMUN_LIST_LEVELS - 1));
20969
- const suppress = gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
20970
- const numberer = new GongmunNumberer(gongmun.numbering);
21019
+ const suppress = gongmun.suppressSingle && gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
21020
+ const numberer = new GongmunNumberer(gongmun.numbering, gongmun.bullet2, gongmun.preset === "press");
20971
21021
  run.forEach((bi, k) => {
20972
21022
  const marker = numberer.next(depths[k], suppress[k]);
20973
21023
  result.set(bi, { marker, depth: depths[k] });
@@ -20997,7 +21047,8 @@ function cell(opts) {
20997
21047
  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>`;
20998
21048
  }
20999
21049
  function table(rows, w, h, cols, tblBf = 1) {
21000
- 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>`;
21050
+ const outLR = w + 566 > 48e3 ? 0 : 283;
21051
+ 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>`;
21001
21052
  }
21002
21053
  function buildGaejosikCover(title, gongmun) {
21003
21054
  const g = coverGeom(gongmun.bodyHeight, gongmun.sizes);
@@ -21660,7 +21711,7 @@ function profileColWidths(tp, colCnt) {
21660
21711
  if (tp.width) return Array(colCnt).fill(Math.floor(tp.width / colCnt));
21661
21712
  return null;
21662
21713
  }
21663
- var CELL_PAD = 582;
21714
+ var CELL_PAD = 1200;
21664
21715
  function cellContentWidth(text, charHeight) {
21665
21716
  let max = 0;
21666
21717
  for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
@@ -21669,35 +21720,51 @@ function cellContentWidth(text, charHeight) {
21669
21720
  }
21670
21721
  return max;
21671
21722
  }
21672
- function computeColWidths(colMax, totalWidth) {
21723
+ function cellMinWordWidth(text, charHeight) {
21724
+ let max = 0;
21725
+ for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
21726
+ for (const word of seg.trim().split(/\s+/)) {
21727
+ const w = measureTextWidth(word, charHeight, 100);
21728
+ if (w > max) max = w;
21729
+ }
21730
+ }
21731
+ return max;
21732
+ }
21733
+ function computeColWidths(colMax, totalWidth, colMinWord = []) {
21673
21734
  const colCnt = colMax.length;
21674
21735
  const minW = Math.min(Math.max(2e3, Math.round(totalWidth * 0.06)), Math.floor(totalWidth / colCnt));
21675
21736
  const cap = Math.round(totalWidth * 0.8);
21676
21737
  const raw = colMax.map((w) => Math.min(Math.max(w + CELL_PAD, minW), cap));
21738
+ const floor = raw.map((r, i) => Math.min(Math.max(minW, (colMinWord[i] ?? 0) + CELL_PAD), r, cap));
21677
21739
  const widths = Array(colCnt).fill(0);
21678
21740
  const free = new Set(raw.map((_, i) => i));
21679
21741
  let budget = totalWidth;
21680
- const evenShare = Math.floor(totalWidth / colCnt);
21681
- for (let i = 0; i < colCnt; i++) {
21682
- const fixed = Math.min(Math.round(raw[i] * 1.12), evenShare);
21683
- if (raw[i] <= evenShare && free.size > 1) {
21684
- widths[i] = fixed;
21685
- free.delete(i);
21686
- budget -= fixed;
21687
- }
21688
- }
21689
- if (free.size === 0) {
21742
+ let sumFloorFree = floor.reduce((a, b) => a + b, 0);
21743
+ for (const i of [...raw.keys()].sort((a, b) => raw[a] - raw[b])) {
21744
+ if (free.size <= 1) break;
21745
+ const fixed = Math.round(raw[i] * 1.12);
21746
+ if (budget - fixed < sumFloorFree - floor[i]) break;
21747
+ widths[i] = fixed;
21748
+ free.delete(i);
21749
+ budget -= fixed;
21750
+ sumFloorFree -= floor[i];
21751
+ }
21752
+ const sumFreeRaw = [...free].reduce((a, i) => a + raw[i], 0);
21753
+ if (budget > sumFreeRaw * 1.6) {
21690
21754
  const sumRaw = raw.reduce((a, b) => a + b, 0);
21691
- for (let i = 0; i < colCnt; i++) widths[i] += Math.floor(raw[i] / sumRaw * (totalWidth - sumRaw));
21755
+ for (let i = 0; i < colCnt; i++) widths[i] = raw[i] + Math.floor(raw[i] / sumRaw * (totalWidth - sumRaw));
21756
+ free.clear();
21692
21757
  } else {
21693
21758
  for (; ; ) {
21694
21759
  const sum2 = [...free].reduce((a, i) => a + raw[i], 0);
21695
- const short = [...free].filter((i) => raw[i] / sum2 * budget < minW);
21760
+ const short = [...free].filter((i) => raw[i] / sum2 * budget < floor[i]);
21696
21761
  if (short.length === 0) break;
21762
+ const shortSum = short.reduce((a, i) => a + floor[i], 0);
21763
+ const scale = Math.min(1, (budget - (free.size - short.length) * minW) / shortSum);
21697
21764
  for (const i of short) {
21698
- widths[i] = minW;
21765
+ widths[i] = Math.max(minW, Math.floor(floor[i] * scale));
21699
21766
  free.delete(i);
21700
- budget -= minW;
21767
+ budget -= widths[i];
21701
21768
  }
21702
21769
  if (free.size === 0) break;
21703
21770
  }
@@ -21730,12 +21797,15 @@ function generateTable(rows, theme, style = null, remap = null, seq = 0) {
21730
21797
  const prof = takeProfile(remap, rowCnt, colCnt, anchorOfMarkdownCell(rows[0]?.[0] ?? ""), seq);
21731
21798
  const colMax = Array(colCnt).fill(0);
21732
21799
  const colMaxBody = Array(colCnt).fill(0);
21800
+ const colMinWord = Array(colCnt).fill(0);
21733
21801
  rows.forEach((row, r) => row.forEach((cell2, c) => {
21734
21802
  const w = cellContentWidth(cell2, measureH);
21735
21803
  if (w > colMax[c]) colMax[c] = w;
21736
21804
  if (r > 0 && w > colMaxBody[c]) colMaxBody[c] = w;
21805
+ const mw = cellMinWordWidth(cell2, measureH);
21806
+ if (mw > colMinWord[c]) colMinWord[c] = mw;
21737
21807
  }));
21738
- const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalW);
21808
+ const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalW, colMinWord);
21739
21809
  const colCentered = colWidths.map((w, c) => colMaxBody[c] + CELL_PAD <= w);
21740
21810
  const labelCol0 = !!reg && colCnt === 2 && colCentered[0] && rows.every((r) => (r[0] ?? "").replace(/\*\*|__|`/g, "").length <= 12);
21741
21811
  const tblId = nextTableId();
@@ -21814,16 +21884,19 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
21814
21884
  const prof = takeProfile(remap, rowCnt, colCnt, first ? anchorOfHtmlCell(first.inner) : "", seq);
21815
21885
  const colMax = Array(colCnt).fill(0);
21816
21886
  const colMaxBody = Array(colCnt).fill(0);
21887
+ const colMinWord = Array(colCnt).fill(0);
21817
21888
  const cellLines = placed.map((cell2) => htmlCellInnerToLines(cell2.inner).lines);
21818
21889
  placed.forEach((cell2, i) => {
21819
21890
  const w = Math.max(...cellLines[i].map((l) => measureTextWidth(unescapeHtml(l).trim(), measureH, 100)), 0) / cell2.colSpan;
21891
+ const mw = cell2.colSpan === 1 ? Math.max(...cellLines[i].map((l) => cellMinWordWidth(unescapeHtml(l), measureH)), 0) : 0;
21820
21892
  for (let dc = 0; dc < cell2.colSpan; dc++) {
21821
21893
  const c = cell2.c + dc;
21822
21894
  if (w > colMax[c]) colMax[c] = w;
21823
21895
  if (!cell2.isHeader && w > colMaxBody[c]) colMaxBody[c] = w;
21896
+ if (dc === 0 && mw > colMinWord[c]) colMinWord[c] = mw;
21824
21897
  }
21825
21898
  });
21826
- const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalWidth);
21899
+ const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalWidth, colMinWord);
21827
21900
  const colCentered = colWidths.map((w, c) => colMaxBody[c] + CELL_PAD <= w);
21828
21901
  const cellH = style ? Math.round(measureH * 1.6) + 282 : 1500;
21829
21902
  const tblW = colWidths.reduce((a, b) => a + b, 0);
@@ -21935,6 +22008,118 @@ function buildTitleBox(title, titleCharPr, bodyWidth, reg) {
21935
22008
  return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
21936
22009
  }
21937
22010
 
22011
+ // src/hwpx/gen-docframe.ts
22012
+ function docframeIds(base) {
22013
+ 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 };
22014
+ }
22015
+ function docframeActive(g) {
22016
+ return !!(g.docHead || g.docFoot || g.reportInfo || g.noticeHead || g.press);
22017
+ }
22018
+ function docframeCharPrXmls(base, measured) {
22019
+ const body = measured ? 4 : 0;
22020
+ const ids = docframeIds(base);
22021
+ return [
22022
+ charPr(ids.org, 1800, true, false, body),
22023
+ charPr(ids.sender, 2200, true, false, body),
22024
+ charPr(ids.foot, 900, false, false, body),
22025
+ charPr(ids.small, 1200, false, false, body),
22026
+ charPr(ids.pressLabel, 2e3, true, false, body),
22027
+ charPr(ids.pressHead, 1e3, true, false, body),
22028
+ charPr(ids.pressTitle, 2500, true, false, body),
22029
+ charPr(ids.pressSub, 1500, true, false, body),
22030
+ charPr(ids.pressContact, 1e3, false, false, body)
22031
+ ];
22032
+ }
22033
+ function labeled(label, value, paraPrId) {
22034
+ 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>`;
22035
+ }
22036
+ function blank() {
22037
+ return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t></hp:t></hp:run></hp:p>`;
22038
+ }
22039
+ function buildDocHead(g, ids) {
22040
+ const h = g.docHead;
22041
+ const out = [];
22042
+ if (h.org) {
22043
+ out.push(para(h.org, GONGMUN_CENTER, ids.org), blank());
22044
+ }
22045
+ if (h.to !== void 0) out.push(labeled("\uC218\uC2E0 ", h.to, PARA_NORMAL));
22046
+ out.push(para(h.via ? `(\uACBD\uC720) ${h.via}` : "(\uACBD\uC720)", PARA_NORMAL, CHAR_NORMAL));
22047
+ if (h.title !== void 0) out.push(labeled("\uC81C\uBAA9 ", h.title, PARA_NORMAL));
22048
+ out.push(blank());
22049
+ return out;
22050
+ }
22051
+ var FOOT_SEP = "\u2500".repeat(46);
22052
+ function buildDocFoot(g, ids) {
22053
+ const f = g.docFoot;
22054
+ const out = [blank()];
22055
+ if (f.sender) {
22056
+ out.push(para(f.sender, GONGMUN_CENTER, ids.sender), blank());
22057
+ }
22058
+ const foot = (text) => para(text, GONGMUN_TBL_LEFT, ids.foot);
22059
+ out.push(foot(FOOT_SEP));
22060
+ if (f.drafter || f.reviewer || f.approver)
22061
+ out.push(foot(`\uAE30\uC548\uC790 ${f.drafter ?? ""} \uAC80\uD1A0\uC790 ${f.reviewer ?? ""} \uACB0\uC7AC\uAD8C\uC790 ${f.approver ?? ""}`));
22062
+ if (f.cooperator) out.push(foot(`\uD611\uC870\uC790 ${f.cooperator}`));
22063
+ if (f.docNum || f.receive) out.push(foot(`\uC2DC\uD589 ${f.docNum ?? ""} \uC811\uC218 ${f.receive ?? ""}`));
22064
+ if (f.address || f.site) out.push(foot(`${f.address ?? ""} / ${f.site ?? ""}`));
22065
+ if (f.phone || f.fax || f.email || f.disclosure)
22066
+ out.push(foot(`\uC804\uD654 ${f.phone ?? ""} \uC804\uC1A1 ${f.fax ?? ""} / ${f.email ?? ""} / ${f.disclosure ?? ""}`));
22067
+ return out;
22068
+ }
22069
+ function buildReportInfo(g, ids) {
22070
+ return para(g.reportInfo, GONGMUN_RIGHT, ids.small);
22071
+ }
22072
+ function buildNoticeHead(g) {
22073
+ const n = g.noticeHead;
22074
+ return n.no ? [para(n.no, PARA_NORMAL, CHAR_BOLD), blank()] : [];
22075
+ }
22076
+ function buildNoticeFoot(g) {
22077
+ const n = g.noticeHead;
22078
+ const out = [];
22079
+ if (n.date) out.push(blank(), para(n.date, GONGMUN_RIGHT, CHAR_NORMAL));
22080
+ if (n.sender) out.push(blank(), para(n.sender, GONGMUN_RIGHT, CHAR_BOLD));
22081
+ return out;
22082
+ }
22083
+ var PRESS_LABEL_H = 3200;
22084
+ var PRESS_HEAD_H = 900;
22085
+ function buildPressHead(g, ids, bodyWidth, reg) {
22086
+ const p = g.press;
22087
+ const w = bodyWidth - 280;
22088
+ const thick = { t: "thick", b: "thick", l: "thick", r: "thick" };
22089
+ 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) })];
22090
+ const headLine = [
22091
+ p.release ? `\uBCF4\uB3C4\uC2DC\uC810 : ${p.release}` : "",
22092
+ p.distribute ? `\uBC30\uD3EC : ${p.distribute}` : ""
22093
+ ].filter(Boolean).join(" / ");
22094
+ 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) }));
22095
+ const table2 = tbl(rows, w, PRESS_LABEL_H + (headLine ? PRESS_HEAD_H : 0), 1);
22096
+ return [`<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`, blank()];
22097
+ }
22098
+ function buildPressContact(g, ids, bodyWidth, reg) {
22099
+ const c = g.press?.contact;
22100
+ if (!c || !c.dept && !c.manager && !c.phone) return [];
22101
+ const w = bodyWidth - 280;
22102
+ const cells = [["\uB2F4\uB2F9 \uBD80\uC11C", c.dept ?? ""], ["\uB2F4\uB2F9\uC790", c.manager ?? ""], ["\uC5F0\uB77D\uCC98", c.phone ?? ""]];
22103
+ const labelW = Math.round(w * 0.14);
22104
+ const valueW = Math.round(w / 3) - labelW;
22105
+ let col = 0;
22106
+ const row = cells.flatMap(([label, value], i) => {
22107
+ const bf = (first) => reg.get({
22108
+ t: "thick",
22109
+ b: "thick",
22110
+ l: first && i === 0 ? "thick" : "thin",
22111
+ r: !first && i === cells.length - 1 ? "thick" : "thin",
22112
+ fill: first ? "#E6E6E6" : void 0
22113
+ });
22114
+ return [
22115
+ tc({ bf: bf(true), row: 0, col: col++, w: labelW, h: 1100, paras: para(label, GONGMUN_TBL_CENTER, ids.pressHead) }),
22116
+ tc({ bf: bf(false), row: 0, col: col++, w: valueW, h: 1100, paras: para(value, GONGMUN_TBL_CENTER, ids.pressContact) })
22117
+ ];
22118
+ });
22119
+ const table2 = tbl([row.join("")], w, 1100, cells.length * 2);
22120
+ return [blank(), `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`];
22121
+ }
22122
+
21938
22123
  // src/hwpx/equation-generate.ts
21939
22124
  var MAX_EQUATION_SOURCE = 1e4;
21940
22125
  var MAX_GROUP_DEPTH = 64;
@@ -22227,21 +22412,25 @@ function generateSecPr(gongmun) {
22227
22412
  m.footer = gongmun.headerFooter;
22228
22413
  }
22229
22414
  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>`;
22230
- return gongmun?.pageNumbers ? secPr + pageNumCtrl() : secPr;
22415
+ const colPr = `<hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
22416
+ return secPr + colPr + (gongmun?.pageNumbers ? pageNumCtrl() : "");
22231
22417
  }
22232
- function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null) {
22418
+ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null, dfIds = null) {
22233
22419
  const paraXmls = [];
22234
22420
  let isFirst = true;
22235
22421
  let tableSeq = 0;
22236
22422
  const orderedCounters = {};
22237
22423
  let prevWasOrdered = false;
22238
22424
  const gaejosik = gongmun?.preset === "gaejosik";
22239
- const vBase = charVariantBase(gaejosik);
22425
+ const measured = !!gongmun && usesReportFonts(gongmun.preset);
22426
+ const vBase = charVariantBase(measured, !!gongmun);
22240
22427
  const tableStyle = gongmun ? {
22241
22428
  totalWidth: mmToHwpunit(210 - gongmun.margins.left - gongmun.margins.right),
22242
- charPr: gaejosik ? GJ_CHAR_TABLE : CHAR_NORMAL,
22243
- boldCharPr: gaejosik ? GJ_CHAR_TABLE_BOLD : CHAR_BOLD,
22244
- charHeight: gaejosik ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : gongmun.bodyHeight,
22429
+ // 크기 실측: 실측 프리셋 맑은 고딕 12pt(22·23), 비실측도 12pt(11·12,
22430
+ // 실결재 지배값 본문 크기 셀은 열폭 부족으로 서술 열 세로 신장, v4.0.2 QA)
22431
+ charPr: measured ? GJ_CHAR_TABLE : GONGMUN_TBL_CHAR,
22432
+ boldCharPr: measured ? GJ_CHAR_TABLE_BOLD : GONGMUN_TBL_CHAR_BOLD,
22433
+ charHeight: measured ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : GONGMUN_TBL_PT,
22245
22434
  headerBf: gongmunTableHeaderBf(gaejosik),
22246
22435
  centerParaPr: GONGMUN_CENTER,
22247
22436
  tblCenterParaPr: GONGMUN_TBL_CENTER,
@@ -22251,15 +22440,29 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22251
22440
  } : null;
22252
22441
  const chamMap = (id) => id === CHAR_BOLD ? GJ_CHAR_CHAM_BOLD : id;
22253
22442
  let chapterNo = 0;
22443
+ let h2Seq = 0;
22254
22444
  let coverH1Idx = -1;
22255
22445
  let pendingPageBreak = false;
22256
22446
  let pendingNewNum = false;
22257
22447
  let titleBoxH1Idx = -1;
22448
+ let pressH1Idx = -1;
22258
22449
  let hasFrontPages = false;
22259
22450
  const preamble = [];
22451
+ if (gongmun?.reportInfo && dfIds) {
22452
+ preamble.push(buildReportInfo(gongmun, dfIds));
22453
+ }
22260
22454
  if (gongmun?.approval && bfReg) {
22261
22455
  preamble.push(buildApprovalTable(gongmun.approval, gongmun, bfReg));
22262
22456
  }
22457
+ if (gongmun?.docHead && dfIds) {
22458
+ preamble.push(...buildDocHead(gongmun, dfIds));
22459
+ }
22460
+ if (gongmun?.noticeHead && dfIds) {
22461
+ preamble.push(...buildNoticeHead(gongmun));
22462
+ }
22463
+ if (gongmun?.press && dfIds && bfReg && tableStyle) {
22464
+ preamble.push(...buildPressHead(gongmun, dfIds, tableStyle.totalWidth, bfReg));
22465
+ }
22263
22466
  if (gaejosik && gongmun) {
22264
22467
  const h1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
22265
22468
  const chapters = blocks.filter((b) => b.type === "heading" && b.level === 2).map((b) => stripChapterNumber(b.text || ""));
@@ -22284,6 +22487,8 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22284
22487
  }
22285
22488
  } else if (gongmun && !gaejosik && bfReg && (gongmun.preset === "report" || gongmun.preset === "plan" || gongmun.preset === "notice")) {
22286
22489
  titleBoxH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
22490
+ } else if (gongmun?.press && dfIds) {
22491
+ pressH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
22287
22492
  }
22288
22493
  if (preamble.length > 0) {
22289
22494
  preamble[0] = preamble[0].replace(` pageBreak="1"`, "").replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${generateSecPr(gongmun)}`);
@@ -22303,8 +22508,16 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22303
22508
  }
22304
22509
  switch (block.type) {
22305
22510
  case "heading": {
22511
+ if (gongmun && blockIdx === pressH1Idx && dfIds) {
22512
+ const parts = [generateParagraph((block.text || "").trim(), GONGMUN_CENTER, dfIds.pressTitle)];
22513
+ for (const s of gongmun.press?.sub ?? []) {
22514
+ parts.push(generateParagraph(`- ${s} -`, GONGMUN_CENTER, dfIds.pressSub));
22515
+ }
22516
+ xml = parts.join("\n ");
22517
+ break;
22518
+ }
22306
22519
  if (gongmun && blockIdx === titleBoxH1Idx && tableStyle && bfReg) {
22307
- xml = buildTitleBox((block.text || "").trim(), CHAR_H1, tableStyle.totalWidth, bfReg);
22520
+ xml = buildTitleBox((block.text || "").trim(), measured ? GJ_CHAR_BODY_TITLE : CHAR_H1, tableStyle.totalWidth, bfReg);
22308
22521
  break;
22309
22522
  }
22310
22523
  if (gaejosik) {
@@ -22321,18 +22534,24 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22321
22534
  (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id
22322
22535
  );
22323
22536
  } else {
22324
- xml = generateParagraph(`\u25CB ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
22537
+ xml = generateParagraph(`${gongmun.bullet2} ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
22325
22538
  }
22326
22539
  break;
22327
22540
  }
22328
22541
  const pId = headingParaPrId(block.level || 1);
22329
22542
  const cId = headingCharPrId(block.level || 1);
22330
22543
  const styleId = gongmun ? Math.min(block.level || 1, 4) : 0;
22331
- xml = generateParagraph(block.text || "", pId, cId, void 0, styleId);
22544
+ let hText = block.text || "";
22545
+ if (gongmun && (block.level || 1) === 2 && gongmun.h2Marker !== "none") {
22546
+ const title = stripChapterNumber(hText);
22547
+ hText = gongmun.h2Marker === "box" ? `\u25A1 ${title}` : `${++h2Seq}. ${title}`;
22548
+ }
22549
+ xml = generateParagraph(hText, pId, cId, void 0, styleId);
22332
22550
  break;
22333
22551
  }
22334
22552
  case "paragraph": {
22335
- if (gaejosik && (block.text || "").trimStart().startsWith("\u203B")) {
22553
+ const pTrim = (block.text || "").trimStart();
22554
+ if (measured && (pTrim.startsWith("\u203B") || /^\*\s/.test(pTrim))) {
22336
22555
  xml = generateParagraph((block.text || "").trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
22337
22556
  break;
22338
22557
  }
@@ -22377,7 +22596,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22377
22596
  break;
22378
22597
  }
22379
22598
  case "blockquote": {
22380
- if (gaejosik) {
22599
+ if (measured) {
22381
22600
  const t = (block.text || "").trim();
22382
22601
  if (t) xml = generateParagraph(t.startsWith("\u203B") ? t : `\u203B ${t}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
22383
22602
  break;
@@ -22395,18 +22614,30 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22395
22614
  const depth = info?.depth ?? 0;
22396
22615
  const marker2 = info?.marker ?? "";
22397
22616
  const content = block.text || "";
22398
- if (gaejosik && content.trimStart().startsWith("\u203B")) {
22617
+ if (measured && content.trimStart().startsWith("\u203B")) {
22399
22618
  xml = generateParagraph(content.trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
22400
22619
  break;
22401
22620
  }
22621
+ if (measured && block.marker === "*") {
22622
+ xml = generateParagraph(`* ${content.trim()}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
22623
+ break;
22624
+ }
22402
22625
  const text = marker2 ? `${marker2} ${content}` : content;
22626
+ const listParaPr = marker2 ? GONGMUN_LIST_BASE + depth : GONGMUN_LIST_PLAIN_BASE + depth;
22403
22627
  let listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
22404
22628
  let mapId = fit ? variantMapper(fit, blockIdx, vBase) : void 0;
22405
- if (gaejosik && depth === 0) {
22629
+ if (measured && depth === 0 && gongmun.numbering !== "standard") {
22406
22630
  listCharPr = GJ_CHAR_DAE;
22407
22631
  mapId = (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id;
22408
22632
  }
22409
- xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr, mapId);
22633
+ if (gongmun.preset === "press") {
22634
+ if (depth === 0) listCharPr = CHAR_NORMAL;
22635
+ if (depth >= 2 && dfIds) {
22636
+ listCharPr = dfIds.small;
22637
+ mapId = void 0;
22638
+ }
22639
+ }
22640
+ xml = generateParagraph(text, listParaPr, listCharPr, mapId);
22410
22641
  break;
22411
22642
  }
22412
22643
  const indent = block.indent || 0;
@@ -22480,10 +22711,19 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22480
22711
  }
22481
22712
  paraXmls.push(xml);
22482
22713
  }
22714
+ if (gongmun?.noticeHead && dfIds) {
22715
+ paraXmls.push(...buildNoticeFoot(gongmun));
22716
+ }
22483
22717
  if (gongmun?.endMark && paraXmls.length > 0) {
22484
22718
  const lastText = [...blocks].reverse().find((b) => (b.type === "paragraph" || b.type === "list_item") && (b.text || "").trim())?.text ?? "";
22485
22719
  if (!hasEndMark(lastText)) paraXmls.push(buildEndMark());
22486
22720
  }
22721
+ if (gongmun?.docFoot && dfIds) {
22722
+ paraXmls.push(...buildDocFoot(gongmun, dfIds));
22723
+ }
22724
+ if (gongmun?.press && dfIds && bfReg && tableStyle) {
22725
+ paraXmls.push(...buildPressContact(gongmun, dfIds, tableStyle.totalWidth, bfReg));
22726
+ }
22487
22727
  if (paraXmls.length === 0) {
22488
22728
  paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run></hp:p>`);
22489
22729
  }
@@ -22498,14 +22738,18 @@ async function markdownToHwpx(markdown, options) {
22498
22738
  const theme = resolveTheme(options?.theme);
22499
22739
  const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
22500
22740
  const gaejosik = gongmun?.preset === "gaejosik";
22741
+ const measured = !!gongmun && usesReportFonts(gongmun.preset);
22501
22742
  const blocks = parseMarkdownToBlocks(markdown);
22502
22743
  const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
22503
22744
  const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
22504
22745
  const staticBfEnd = gongmun ? gaejosik ? 11 : 4 : 3;
22505
- const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(gaejosik) + (fit?.variants?.length ?? 0) * 4, staticBfEnd) : null;
22746
+ const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(measured, !!gongmun) + (fit?.variants?.length ?? 0) * 4, staticBfEnd) : null;
22506
22747
  const bfReg = gongmun ? new TableBfRegistry(staticBfEnd + (remap?.borderFillXmls.length ?? 0)) : null;
22748
+ const dfActive = !!gongmun && docframeActive(gongmun);
22749
+ const dfBase = charVariantBase(measured, !!gongmun) + (fit?.variants?.length ?? 0) * 4 + (remap?.charPrXmls.length ?? 0);
22750
+ const dfIds = dfActive ? docframeIds(dfBase) : null;
22507
22751
  const chartParts = [];
22508
- const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap);
22752
+ const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap, dfIds);
22509
22753
  if (remap && remap.tables.length > 0) {
22510
22754
  const unused = remap.tables.filter((t) => !t.used).length;
22511
22755
  if (unused === remap.tables.length) {
@@ -22517,7 +22761,13 @@ async function markdownToHwpx(markdown, options) {
22517
22761
  zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
22518
22762
  zip.file("META-INF/container.xml", generateContainerXml());
22519
22763
  zip.file("Contents/content.hpf", generateManifest(chartParts));
22520
- zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? [], extraBorderFills, remap?.charPrXmls ?? []));
22764
+ zip.file("Contents/header.xml", generateHeaderXml(
22765
+ theme,
22766
+ gongmun,
22767
+ fit?.variants ?? [],
22768
+ extraBorderFills,
22769
+ [...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase, measured) : []]
22770
+ ));
22521
22771
  zip.file("Contents/section0.xml", sectionXml);
22522
22772
  for (const part of chartParts) zip.file(part.name, part.xml);
22523
22773
  zip.file("Preview/PrvText.txt", buildPrvText(blocks));
@@ -23003,6 +23253,142 @@ function unknownFontWarnings(fonts) {
23003
23253
  return warnings;
23004
23254
  }
23005
23255
 
23256
+ // src/hwpx/gongmun-lint.ts
23257
+ var RULES = [
23258
+ // 날짜 ─ 온점 뒤 한 칸, 0 패딩 금지, 연도 4자리, 끝 마침표
23259
+ {
23260
+ code: "DATE_NO_SPACE",
23261
+ severity: "error",
23262
+ pattern: /\b\d{4}\.\d{1,2}\.\d{1,2}\.?/g,
23263
+ message: "\uB0A0\uC9DC \uC628\uC810 \uB4A4\uC5D0 \uD55C \uCE78\uC529 \uB744\uC6CC\uC57C \uD568",
23264
+ suggest: "\uC608) 2025. 1. 6."
23265
+ },
23266
+ {
23267
+ code: "DATE_ZERO_PAD",
23268
+ severity: "error",
23269
+ pattern: /\b\d{4}\.\s*0\d\.|\.\s*0\d\./g,
23270
+ message: "\uC6D4\xB7\uC77C \uC55E\uC758 '0'\uC740 \uD45C\uAE30\uD558\uC9C0 \uC54A\uC74C",
23271
+ suggest: "\uC608) 2025. 1. 6. (2025. 01. 06. \u2715)"
23272
+ },
23273
+ {
23274
+ code: "DATE_2DIGIT_YR",
23275
+ severity: "error",
23276
+ pattern: /(?<!\d)['’]\d{2}\.\s*\d/g,
23277
+ message: "\uC5F0\uB3C4\uB294 \uB124 \uC790\uB9AC\uB85C \uD45C\uAE30('24 \u2715)",
23278
+ suggest: "\uC608) 2025. 1. 6."
23279
+ },
23280
+ {
23281
+ code: "DATE_NO_END_DOT",
23282
+ severity: "warning",
23283
+ pattern: /\b\d{4}\.\s\d{1,2}\.\s\d{1,2}(?!\s*[.\d(])/g,
23284
+ message: "\uB0A0\uC9DC\uC758 '\uC77C' \uB2E4\uC74C\uC5D0 \uB9C8\uCE68\uD45C(.)\uB97C \uCC0D\uC5B4\uC57C \uD568",
23285
+ suggest: "\uC608) 2025. 1. 6."
23286
+ },
23287
+ // 시간 ─ 24시각제, 쌍점 붙여쓰기
23288
+ {
23289
+ code: "TIME_AMPM",
23290
+ severity: "error",
23291
+ pattern: /(오전|오후|아침|밤|낮)\s*\d{1,2}\s*시/g,
23292
+ message: "24\uC2DC\uAC01\uC81C \uC22B\uC790\uB85C \uD45C\uAE30(\uC624\uC804/\uC624\uD6C4 \uC0AC\uC6A9 \uC548 \uD568)",
23293
+ suggest: "\uC608) 09:00, 15:30"
23294
+ },
23295
+ {
23296
+ code: "TIME_24H",
23297
+ severity: "warning",
23298
+ pattern: /(?<!\d)24\s*시(?!각)/g,
23299
+ message: "'24\uC2DC'\uBCF4\uB2E4 \uC775\uC77C 00:00 \uB610\uB294 '18:00\uAE4C\uC9C0' \uAD8C\uC7A5",
23300
+ suggest: "\uC608) 18:00"
23301
+ },
23302
+ {
23303
+ code: "TIME_COLON_SP",
23304
+ severity: "error",
23305
+ pattern: /\b\d{1,2}\s+:\s*\d{2}\b|\b\d{1,2}:\s+\d{2}\b/g,
23306
+ message: "\uC2DC\uC640 \uBD84 \uC0AC\uC774 \uC30D\uC810\uC740 \uC591\uCABD\uC744 \uBD99\uC5EC \uC500",
23307
+ suggest: "\uC608) 13:20"
23308
+ },
23309
+ // 금액 ─ '천원' 금지, 금+숫자 붙여쓰기
23310
+ {
23311
+ code: "MONEY_CHEONWON",
23312
+ severity: "error",
23313
+ pattern: /\d+\s*천\s*원/g,
23314
+ message: "\uAE08\uC561\uC740 '\uCC9C\uC6D0'\uC73C\uB85C \uC904\uC774\uC9C0 \uC54A\uACE0 \uC544\uB77C\uBE44\uC544 \uC22B\uC790\uB85C",
23315
+ suggest: "\uC608) 345,000\uC6D0"
23316
+ },
23317
+ {
23318
+ code: "MONEY_GEUM_SP",
23319
+ severity: "warning",
23320
+ pattern: /금\s+\d/g,
23321
+ message: "'\uAE08'\uACFC \uC22B\uC790 \uC0AC\uC774\uB294 \uBD99\uC5EC \uC4F0\uB294 \uAC83\uC774 \uC6D0\uCE59",
23322
+ suggest: "\uC608) \uAE08113,560\uC6D0"
23323
+ },
23324
+ // 붙임 ─ 쌍점 금지(2타 띄움)
23325
+ {
23326
+ code: "BUNIM_COLON",
23327
+ severity: "error",
23328
+ pattern: /붙\s*임\s*:/g,
23329
+ message: "'\uBD99\uC784' \uB2E4\uC74C\uC5D0 \uC30D\uC810(:)\uC744 \uBD99\uC774\uC9C0 \uC54A\uC74C(2\uD0C0 \uB744\uC6C0)",
23330
+ suggest: "\uC608) \uBD99\uC784 \uACC4\uD68D\uC11C 1\uBD80."
23331
+ },
23332
+ // 표기 ─ 물결표+까지 중복, 한글 먼저, 쌍점 띄어쓰기
23333
+ {
23334
+ code: "KKAJI_DUP",
23335
+ severity: "error",
23336
+ pattern: /[∼~~][^\n]{0,20}?까지/g,
23337
+ message: "\uBB3C\uACB0\uD45C(\u223C)\uC640 '\uAE4C\uC9C0'\uB97C \uD568\uAED8 \uC4F0\uC9C0 \uC54A\uC74C",
23338
+ suggest: "\uC608) 2. 20.\u223C2. 24."
23339
+ },
23340
+ {
23341
+ code: "FOREIGN_FIRST",
23342
+ severity: "warning",
23343
+ pattern: /\b[A-Z]{2,5}\s*\([가-힣]/g,
23344
+ message: "\uD55C\uAE00\uC744 \uBA3C\uC800 \uC4F0\uACE0 \uAD04\uD638 \uC548\uC5D0 \uC678\uAD6D\uC5B4\uB97C \uBCD1\uAE30",
23345
+ suggest: "\uC608) \uC5C5\uBB34 \uD611\uC57D(MOU)"
23346
+ },
23347
+ // 쌍점 — URL(https:// 등)·시각(13:20)은 제외
23348
+ {
23349
+ code: "COLON_SPACE",
23350
+ severity: "warning",
23351
+ pattern: /\S\s+:\S|\S:(?!\/\/)[^\s\d]/g,
23352
+ message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
23353
+ suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
23354
+ }
23355
+ ];
23356
+ function lintGongmunText(text) {
23357
+ const findings = [];
23358
+ let inFence = false;
23359
+ const lines = text.split(/\r?\n/);
23360
+ for (let i = 0; i < lines.length; i++) {
23361
+ const line = lines[i];
23362
+ if (/^\s*(```|~~~)/.test(line)) {
23363
+ inFence = !inFence;
23364
+ continue;
23365
+ }
23366
+ if (inFence) continue;
23367
+ for (const r of RULES) {
23368
+ r.pattern.lastIndex = 0;
23369
+ for (const m of line.matchAll(r.pattern)) {
23370
+ findings.push({
23371
+ line: i + 1,
23372
+ match: m[0].trim(),
23373
+ rule: r.code,
23374
+ severity: r.severity,
23375
+ message: r.message,
23376
+ suggest: r.suggest
23377
+ });
23378
+ }
23379
+ }
23380
+ }
23381
+ return findings;
23382
+ }
23383
+ function gongmunLintWarnings(text, limit = 10) {
23384
+ const findings = lintGongmunText(text);
23385
+ const shown = findings.slice(0, limit).map(
23386
+ (f) => `\uD45C\uAE30\uBC95 [${f.rule}] L${f.line} "${f.match}" \u2014 ${f.message}${f.suggest ? ` (${f.suggest})` : ""}`
23387
+ );
23388
+ if (findings.length > limit) shown.push(`\uD45C\uAE30\uBC95 \uACBD\uACE0 ${findings.length - limit}\uAC74 \uB354 \uC788\uC74C \u2014 kordoc lint\uB85C \uC804\uCCB4 \uD655\uC778`);
23389
+ return shown;
23390
+ }
23391
+
23006
23392
  // src/roundtrip/patcher.ts
23007
23393
  import JSZip8 from "jszip";
23008
23394
 
@@ -25734,7 +26120,7 @@ async function parseHwp(buffer, options) {
25734
26120
  async function parsePdf(buffer, options) {
25735
26121
  let parsePdfDocument;
25736
26122
  try {
25737
- const mod = await import("./parser-LHUO6UUA.js");
26123
+ const mod = await import("./parser-3HM2VYKK.js");
25738
26124
  parsePdfDocument = mod.parsePdfDocument;
25739
26125
  } catch {
25740
26126
  return {
@@ -25844,6 +26230,8 @@ export {
25844
26230
  hwpxToProfile,
25845
26231
  isKnownFont,
25846
26232
  unknownFontWarnings,
26233
+ lintGongmunText,
26234
+ gongmunLintWarnings,
25847
26235
  patchHwpx,
25848
26236
  patchHwp,
25849
26237
  validateHwpx,
@@ -25864,4 +26252,4 @@ export {
25864
26252
  parseHwpml,
25865
26253
  fillForm
25866
26254
  };
25867
- //# sourceMappingURL=chunk-PGWAF755.js.map
26255
+ //# sourceMappingURL=chunk-AHWRAGXN.js.map