kordoc 3.7.0 → 3.8.0

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.
@@ -25,7 +25,7 @@ import {
25
25
  sanitizeHref,
26
26
  stripDtd,
27
27
  toArrayBuffer
28
- } from "./chunk-IJJMVTU5.js";
28
+ } from "./chunk-3R3YK7EM.js";
29
29
  import {
30
30
  parsePageRange
31
31
  } from "./chunk-MOL7MDBG.js";
@@ -967,57 +967,62 @@ async function extractImagesFromZip(zip, blocks, decompressed, warnings) {
967
967
  let imageIndex = 0;
968
968
  const imageBlocks = [];
969
969
  collectImageBlocks(blocks, imageBlocks);
970
+ const resolved = /* @__PURE__ */ new Map();
970
971
  for (const { block, ownerCell } of imageBlocks) {
971
972
  if (block.type !== "image" || !block.text) continue;
972
973
  const ref = block.text;
973
- const candidates = [
974
- `BinData/${ref}`,
975
- `Contents/BinData/${ref}`,
976
- ref
977
- // 절대 경로일 수도 있음
978
- ];
979
- let resolvedPath = null;
980
- if (!ref.includes(".")) {
981
- const prefixes = [`BinData/${ref}`, `Contents/BinData/${ref}`];
982
- for (const prefix of prefixes) {
983
- const match = zip.file(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.[a-zA-Z0-9]+$`));
984
- if (match.length > 0) {
985
- resolvedPath = match[0].name;
986
- break;
974
+ let img = resolved.get(ref);
975
+ if (img === void 0) {
976
+ img = null;
977
+ const candidates = [
978
+ `BinData/${ref}`,
979
+ `Contents/BinData/${ref}`,
980
+ ref
981
+ // 절대 경로일 수도 있음
982
+ ];
983
+ let resolvedPath = null;
984
+ if (!ref.includes(".")) {
985
+ const prefixes = [`BinData/${ref}`, `Contents/BinData/${ref}`];
986
+ for (const prefix of prefixes) {
987
+ const match = zip.file(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.[a-zA-Z0-9]+$`));
988
+ if (match.length > 0) {
989
+ resolvedPath = match[0].name;
990
+ break;
991
+ }
987
992
  }
988
993
  }
989
- }
990
- let found = false;
991
- const allCandidates = resolvedPath ? [resolvedPath, ...candidates] : candidates;
992
- for (const path of allCandidates) {
993
- if (isPathTraversal(path)) continue;
994
- const file = zip.file(path);
995
- if (!file) continue;
996
- try {
997
- const data = await file.async("uint8array");
998
- decompressed.total += data.length;
999
- if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError("ZIP \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (ZIP bomb \uC758\uC2EC)");
1000
- const actualPath = path;
1001
- const ext = actualPath.includes(".") ? actualPath.split(".").pop() || "png" : "png";
1002
- const mimeType = imageExtToMime(ext);
1003
- imageIndex++;
1004
- const filename = `image_${String(imageIndex).padStart(3, "0")}.${mimeToExt(mimeType)}`;
1005
- images.push({ filename, data, mimeType });
1006
- block.text = filename;
1007
- block.imageData = { data, mimeType, filename: ref };
1008
- if (ownerCell) ownerCell.text = ownerCell.text.replace(`![image](${ref})`, `![image](${filename})`);
1009
- found = true;
1010
- break;
1011
- } catch (err) {
1012
- if (err instanceof KordocError) throw err;
994
+ const allCandidates = resolvedPath ? [resolvedPath, ...candidates] : candidates;
995
+ for (const path of allCandidates) {
996
+ if (isPathTraversal(path)) continue;
997
+ const file = zip.file(path);
998
+ if (!file) continue;
999
+ try {
1000
+ const data = await file.async("uint8array");
1001
+ decompressed.total += data.length;
1002
+ if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError("ZIP \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (ZIP bomb \uC758\uC2EC)");
1003
+ const ext = path.includes(".") ? path.split(".").pop() || "png" : "png";
1004
+ const mimeType = imageExtToMime(ext);
1005
+ imageIndex++;
1006
+ const filename = `image_${String(imageIndex).padStart(3, "0")}.${mimeToExt(mimeType)}`;
1007
+ img = { filename, data, mimeType };
1008
+ images.push(img);
1009
+ break;
1010
+ } catch (err) {
1011
+ if (err instanceof KordocError) throw err;
1012
+ }
1013
1013
  }
1014
+ if (!img) warnings?.push({ page: block.pageNumber, message: `\uC774\uBBF8\uC9C0 \uD30C\uC77C \uC5C6\uC74C: ${ref}`, code: "SKIPPED_IMAGE" });
1015
+ resolved.set(ref, img);
1014
1016
  }
1015
- if (!found) {
1016
- warnings?.push({ page: block.pageNumber, message: `\uC774\uBBF8\uC9C0 \uD30C\uC77C \uC5C6\uC74C: ${ref}`, code: "SKIPPED_IMAGE" });
1017
+ if (!img) {
1017
1018
  block.type = "paragraph";
1018
1019
  block.text = `[\uC774\uBBF8\uC9C0: ${ref}]`;
1019
1020
  if (ownerCell) ownerCell.text = ownerCell.text.replace(`![image](${ref})`, `[\uC774\uBBF8\uC9C0: ${ref}]`);
1021
+ continue;
1020
1022
  }
1023
+ block.text = img.filename;
1024
+ block.imageData = { data: img.data, mimeType: img.mimeType, filename: ref };
1025
+ if (ownerCell) ownerCell.text = ownerCell.text.replace(`![image](${ref})`, `![image](${img.filename})`);
1021
1026
  }
1022
1027
  return images;
1023
1028
  }
@@ -2396,32 +2401,42 @@ function resolveImageBlocks(binDataMap, blocks, warnings) {
2396
2401
  if (imageBlocks.length === 0) return [];
2397
2402
  const images = [];
2398
2403
  const renamed = /* @__PURE__ */ new Map();
2404
+ const resolved = /* @__PURE__ */ new Map();
2399
2405
  let imageIndex = 0;
2400
2406
  for (const block of imageBlocks) {
2401
2407
  if (!block.text) continue;
2402
2408
  const storageId = parseInt(block.text, 10);
2403
2409
  if (isNaN(storageId)) continue;
2404
- const bin = binDataMap.get(storageId);
2405
- if (!bin) {
2406
- warnings.push({ page: block.pageNumber, message: `BinData ${storageId} \uC5C6\uC74C`, code: "SKIPPED_IMAGE" });
2407
- block.type = "paragraph";
2408
- block.text = `[\uC774\uBBF8\uC9C0: BinData ${storageId}]`;
2409
- continue;
2410
+ let img = resolved.get(storageId);
2411
+ if (img === void 0) {
2412
+ const bin = binDataMap.get(storageId);
2413
+ if (!bin) {
2414
+ warnings.push({ page: block.pageNumber, message: `BinData ${storageId} \uC5C6\uC74C`, code: "SKIPPED_IMAGE" });
2415
+ resolved.set(storageId, null);
2416
+ } else {
2417
+ const mime = detectImageMime(bin.data);
2418
+ if (!mime) {
2419
+ warnings.push({ page: block.pageNumber, message: `BinData ${storageId}: \uC54C \uC218 \uC5C6\uB294 \uC774\uBBF8\uC9C0 \uD615\uC2DD`, code: "SKIPPED_IMAGE" });
2420
+ resolved.set(storageId, null);
2421
+ } else {
2422
+ imageIndex++;
2423
+ const ext = mime.includes("jpeg") ? "jpg" : mime.includes("png") ? "png" : mime.includes("gif") ? "gif" : mime.includes("bmp") ? "bmp" : "bin";
2424
+ img = { filename: `image_${String(imageIndex).padStart(3, "0")}.${ext}`, data: new Uint8Array(bin.data), mime };
2425
+ resolved.set(storageId, img);
2426
+ images.push({ filename: img.filename, data: img.data, mimeType: img.mime });
2427
+ renamed.set(storageId, img.filename);
2428
+ }
2429
+ }
2430
+ img = resolved.get(storageId);
2410
2431
  }
2411
- const mime = detectImageMime(bin.data);
2412
- if (!mime) {
2413
- warnings.push({ page: block.pageNumber, message: `BinData ${storageId}: \uC54C \uC218 \uC5C6\uB294 \uC774\uBBF8\uC9C0 \uD615\uC2DD`, code: "SKIPPED_IMAGE" });
2432
+ if (!img) {
2433
+ const bin = binDataMap.get(storageId);
2414
2434
  block.type = "paragraph";
2415
- block.text = `[\uC774\uBBF8\uC9C0: ${bin.name}]`;
2435
+ block.text = bin ? `[\uC774\uBBF8\uC9C0: ${bin.name}]` : `[\uC774\uBBF8\uC9C0: BinData ${storageId}]`;
2416
2436
  continue;
2417
2437
  }
2418
- imageIndex++;
2419
- const ext = mime.includes("jpeg") ? "jpg" : mime.includes("png") ? "png" : mime.includes("gif") ? "gif" : mime.includes("bmp") ? "bmp" : "bin";
2420
- const filename = `image_${String(imageIndex).padStart(3, "0")}.${ext}`;
2421
- images.push({ filename, data: new Uint8Array(bin.data), mimeType: mime });
2422
- renamed.set(storageId, filename);
2423
- block.text = filename;
2424
- block.imageData = { data: new Uint8Array(bin.data), mimeType: mime, filename: bin.name };
2438
+ block.text = img.filename;
2439
+ block.imageData = { data: img.data, mimeType: img.mime, filename: binDataMap.get(storageId).name };
2425
2440
  }
2426
2441
  resolveCellImageSentinels(blocks, renamed);
2427
2442
  return images;
@@ -18123,7 +18138,7 @@ function parseTable(tbl, styles, numbering, footnotes, rels) {
18123
18138
  };
18124
18139
  return { type: "table", table };
18125
18140
  }
18126
- async function extractImages(zip, rels, doc) {
18141
+ async function extractImages(zip, rels, doc, warnings) {
18127
18142
  const blocks = [];
18128
18143
  const images = [];
18129
18144
  const drawingElements = findElements(doc.documentElement, "drawing");
@@ -18154,7 +18169,11 @@ async function extractImages(zip, rels, doc) {
18154
18169
  const filename = `image_${String(imgIdx).padStart(3, "0")}.${ext}`;
18155
18170
  images.push({ filename, data, mimeType: mimeMap[ext] ?? "image/png" });
18156
18171
  blocks.push({ type: "image", text: filename });
18157
- } catch {
18172
+ } catch (err) {
18173
+ warnings.push({
18174
+ code: "SKIPPED_IMAGE",
18175
+ message: `DOCX \uC774\uBBF8\uC9C0 \uCD94\uCD9C \uC2E4\uD328 (${imgPath}): ${err instanceof Error ? err.message : String(err)}`
18176
+ });
18158
18177
  }
18159
18178
  }
18160
18179
  }
@@ -18178,7 +18197,11 @@ async function parseDocxDocument(buffer, options) {
18178
18197
  if (stylesFile) {
18179
18198
  try {
18180
18199
  styles = parseStyles(await stylesFile.async("text"));
18181
- } catch {
18200
+ } catch (err) {
18201
+ warnings.push({
18202
+ code: "PARTIAL_PARSE",
18203
+ message: `DOCX \uC2A4\uD0C0\uC77C(styles.xml) \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uAE30\uBCF8 \uC2A4\uD0C0\uC77C\uB85C \uACC4\uC18D: ${err instanceof Error ? err.message : String(err)}`
18204
+ });
18182
18205
  }
18183
18206
  }
18184
18207
  let numbering = /* @__PURE__ */ new Map();
@@ -18186,7 +18209,11 @@ async function parseDocxDocument(buffer, options) {
18186
18209
  if (numFile) {
18187
18210
  try {
18188
18211
  numbering = parseNumbering(await numFile.async("text"));
18189
- } catch {
18212
+ } catch (err) {
18213
+ warnings.push({
18214
+ code: "PARTIAL_PARSE",
18215
+ message: `DOCX \uBC88\uD638\uB9E4\uAE30\uAE30(numbering.xml) \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uBAA9\uB85D \uBC88\uD638 \uC0DD\uB7B5: ${err instanceof Error ? err.message : String(err)}`
18216
+ });
18190
18217
  }
18191
18218
  }
18192
18219
  let footnotes = /* @__PURE__ */ new Map();
@@ -18194,7 +18221,11 @@ async function parseDocxDocument(buffer, options) {
18194
18221
  if (fnFile) {
18195
18222
  try {
18196
18223
  footnotes = parseFootnotes(await fnFile.async("text"));
18197
- } catch {
18224
+ } catch (err) {
18225
+ warnings.push({
18226
+ code: "PARTIAL_PARSE",
18227
+ message: `DOCX \uAC01\uC8FC(footnotes.xml) \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uAC01\uC8FC \uC0DD\uB7B5: ${err instanceof Error ? err.message : String(err)}`
18228
+ });
18198
18229
  }
18199
18230
  }
18200
18231
  const docXml = await docFile.async("text");
@@ -18216,7 +18247,7 @@ async function parseDocxDocument(buffer, options) {
18216
18247
  if (block) blocks.push(block);
18217
18248
  }
18218
18249
  }
18219
- const { blocks: imgBlocks, images } = await extractImages(zip, rels, doc);
18250
+ const { blocks: imgBlocks, images } = await extractImages(zip, rels, doc, warnings);
18220
18251
  const metadata = {};
18221
18252
  const coreFile = zip.file("docProps/core.xml");
18222
18253
  if (coreFile) {
@@ -18234,7 +18265,11 @@ async function parseDocxDocument(buffer, options) {
18234
18265
  if (created) metadata.createdAt = created;
18235
18266
  const modified = getFirst("dcterms:modified");
18236
18267
  if (modified) metadata.modifiedAt = modified;
18237
- } catch {
18268
+ } catch (err) {
18269
+ warnings.push({
18270
+ code: "PARTIAL_PARSE",
18271
+ message: `DOCX \uBA54\uD0C0\uB370\uC774\uD130(core.xml) \uD30C\uC2F1 \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}`
18272
+ });
18238
18273
  }
18239
18274
  }
18240
18275
  const outline = blocks.filter((b) => b.type === "heading").map((b) => ({ level: b.level ?? 2, text: b.text ?? "" }));
@@ -21359,9 +21394,22 @@ function precomputeGongmunList(blocks, gongmun) {
21359
21394
  continue;
21360
21395
  }
21361
21396
  const run = [];
21362
- while (i < blocks.length && blocks[i].type === "list_item") {
21363
- run.push(i);
21364
- i++;
21397
+ while (i < blocks.length) {
21398
+ const t = blocks[i].type;
21399
+ if (t === "list_item") {
21400
+ run.push(i);
21401
+ i++;
21402
+ continue;
21403
+ }
21404
+ if (t === "table" || t === "html_table") {
21405
+ let j = i + 1;
21406
+ while (j < blocks.length && (blocks[j].type === "table" || blocks[j].type === "html_table")) j++;
21407
+ if (j < blocks.length && blocks[j].type === "list_item") {
21408
+ i = j;
21409
+ continue;
21410
+ }
21411
+ }
21412
+ break;
21365
21413
  }
21366
21414
  const depths = run.map((bi) => Math.min(Math.max(blocks[bi].indent || 0, 0), GONGMUN_LIST_LEVELS - 1));
21367
21415
  const suppress = gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
@@ -22930,21 +22978,24 @@ function readRecordsStrict(stream) {
22930
22978
  }
22931
22979
  return recs;
22932
22980
  }
22933
- function serializeRecords(recs, repl) {
22981
+ function serializeRecords(recs, repl, inserts) {
22934
22982
  const parts = [];
22935
- for (let i = 0; i < recs.length; i++) {
22936
- const data = repl?.get(i) ?? recs[i].data;
22983
+ const push = (tagId, level, data) => {
22937
22984
  const ext = data.length >= 4095;
22938
22985
  const header = Buffer.alloc(ext ? 8 : 4);
22939
- header.writeUInt32LE((recs[i].tagId & 1023 | (recs[i].level & 1023) << 10 | (ext ? 4095 : data.length) << 20) >>> 0, 0);
22986
+ header.writeUInt32LE((tagId & 1023 | (level & 1023) << 10 | (ext ? 4095 : data.length) << 20) >>> 0, 0);
22940
22987
  if (ext) header.writeUInt32LE(data.length, 4);
22941
22988
  parts.push(header, data);
22989
+ };
22990
+ for (let i = 0; i < recs.length; i++) {
22991
+ for (const ins of inserts?.get(i) ?? []) push(ins.tagId, ins.level, ins.data);
22992
+ push(recs[i].tagId, recs[i].level, repl?.get(i) ?? recs[i].data);
22942
22993
  }
22943
22994
  return Buffer.concat(parts);
22944
22995
  }
22945
22996
  function scanSection(stream, sectionIndex, compressed) {
22946
22997
  const records = readRecordsStrict(stream);
22947
- if (!records) return { records: [], safe: false, paras: [], tables: [], compressed, repl: /* @__PURE__ */ new Map() };
22998
+ if (!records) return { records: [], safe: false, paras: [], tables: [], compressed, repl: /* @__PURE__ */ new Map(), inserts: /* @__PURE__ */ new Map() };
22948
22999
  const safe = serializeRecords(records).equals(stream);
22949
23000
  const parent = new Int32Array(records.length).fill(-1);
22950
23001
  const stack = [];
@@ -23043,7 +23094,7 @@ function scanSection(stream, sectionIndex, compressed) {
23043
23094
  }
23044
23095
  tables.push({ sectionIndex, rows, cols, cells });
23045
23096
  }
23046
- return { records, safe, paras, tables, compressed, repl: /* @__PURE__ */ new Map() };
23097
+ return { records, safe, paras, tables, compressed, repl: /* @__PURE__ */ new Map(), inserts: /* @__PURE__ */ new Map() };
23047
23098
  }
23048
23099
  async function patchHwp(original, editedMarkdown, options) {
23049
23100
  const skipped = [];
@@ -23111,15 +23162,15 @@ async function patchHwp(original, editedMarkdown, options) {
23111
23162
  }
23112
23163
  }
23113
23164
  let data;
23114
- const dirty = scans.some((s) => s.repl.size > 0);
23165
+ const dirty = scans.some((s) => s.repl.size > 0 || s.inserts.size > 0);
23115
23166
  if (!dirty) {
23116
23167
  data = new Uint8Array(original);
23117
23168
  } else {
23118
23169
  try {
23119
23170
  let out = originalBuf;
23120
23171
  for (let i = 0; i < scans.length; i++) {
23121
- if (scans[i].repl.size === 0) continue;
23122
- const newStream = serializeRecords(scans[i].records, scans[i].repl);
23172
+ if (scans[i].repl.size === 0 && scans[i].inserts.size === 0) continue;
23173
+ const newStream = serializeRecords(scans[i].records, scans[i].repl, scans[i].inserts);
23123
23174
  const content = compressed ? deflateRawSync2(newStream) : newStream;
23124
23175
  out = replaceOleStream(out, sectionPaths[i], content);
23125
23176
  }
@@ -23447,24 +23498,25 @@ function applyCellEdit5(table, scanTable, gridR, gridC, newLines, ctx, before, a
23447
23498
  }
23448
23499
  const unstable = newLines.find((l) => sanitizeText(l) !== l);
23449
23500
  if (unstable !== void 0) return skip("\uACF5\uBC31 \uC815\uADDC\uD654 \uBD88\uC548\uC815 \uD14D\uC2A4\uD2B8 \u2014 \uD328\uCE58 \uC2DC \uC6D0\uBB38 \uBCF4\uC874 \uBD88\uAC00\uB85C \uBBF8\uC9C0\uC6D0");
23450
- if (nonEmpty.length === 0) return skip("\uBE48 \uC140 \uD14D\uC2A4\uD2B8 \uCC44\uC6B0\uAE30\uB294 HWP5 \uBBF8\uC9C0\uC6D0 (v1) \u2014 \uBB38\uB2E8 \uC0DD\uC131 \uD544\uC694");
23501
+ const targets = nonEmpty.length > 0 ? nonEmpty : cell.paras;
23502
+ if (targets.length === 0) return skip("\uC140\uC5D0 \uBB38\uB2E8\uC774 \uC5C6\uC74C \u2014 \uBBF8\uC9C0\uC6D0");
23451
23503
  const assigned = [];
23452
- for (let i = 0; i < nonEmpty.length; i++) {
23504
+ for (let i = 0; i < targets.length; i++) {
23453
23505
  if (i < newLines.length) {
23454
- assigned.push(i === nonEmpty.length - 1 && newLines.length > nonEmpty.length ? newLines.slice(i).join(" ") : newLines[i]);
23506
+ assigned.push(i === targets.length - 1 && newLines.length > targets.length ? newLines.slice(i).join(" ") : newLines[i]);
23455
23507
  } else {
23456
23508
  assigned.push("");
23457
23509
  }
23458
23510
  }
23459
- if (newLines.length > nonEmpty.length) {
23511
+ if (newLines.length > targets.length) {
23460
23512
  ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uBCD1\uD569 \uC801\uC6A9", after: summarize(after), partial: true });
23461
23513
  } else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
23462
23514
  ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uC0AD\uC81C\uB294 \uBB38\uB2E8 \uC81C\uAC70 \uBBF8\uC9C0\uC6D0 \u2014 \uBE48 \uBB38\uB2E8 \uC794\uC874(\uBDF0\uC5B4\uC5D0 \uBE48 \uC904 \uD45C\uC2DC \uAC00\uB2A5)", before: summarize(before), after: summarize(after), partial: true });
23463
23515
  }
23464
23516
  let staged = 0;
23465
- for (let i = 0; i < nonEmpty.length; i++) {
23466
- if (assigned[i] === nonEmpty[i].rawText || normForMatch(assigned[i]) === normForMatch(nonEmpty[i].rawText)) continue;
23467
- staged += stageParaPatch(ctx.scans[nonEmpty[i].sectionIndex], nonEmpty[i], assigned[i], skip);
23517
+ for (let i = 0; i < targets.length; i++) {
23518
+ if (assigned[i] === targets[i].rawText || normForMatch(assigned[i]) === normForMatch(targets[i].rawText)) continue;
23519
+ staged += stageParaPatch(ctx.scans[targets[i].sectionIndex], targets[i], assigned[i], skip);
23468
23520
  }
23469
23521
  return staged > 0 ? 1 : 0;
23470
23522
  }
@@ -23534,7 +23586,21 @@ function splitParaText(data) {
23534
23586
  if (firstP < 0) firstP = k;
23535
23587
  lastP = k;
23536
23588
  }
23537
- if (firstP < 0) return null;
23589
+ if (firstP < 0) {
23590
+ if (toks.some((t) => t.visible)) return null;
23591
+ let cut = data.length;
23592
+ for (const t of toks) if (data.readUInt16LE(t.start) === 13) {
23593
+ cut = t.start;
23594
+ break;
23595
+ }
23596
+ return {
23597
+ prefix: data.subarray(0, cut),
23598
+ prefixUnits: cut / 2,
23599
+ core: "",
23600
+ suffix: data.subarray(cut),
23601
+ suffixUnits: (data.length - cut) / 2
23602
+ };
23603
+ }
23538
23604
  for (let k = firstP; k <= lastP; k++) if (!toks[k].plain) return null;
23539
23605
  for (let k = 0; k < firstP; k++) if (toks[k].visible) return null;
23540
23606
  for (let k = lastP + 1; k < toks.length; k++) if (toks[k].visible) return null;
@@ -23567,7 +23633,6 @@ function rebuildCharShape(csData, coreStartUnit) {
23567
23633
  }
23568
23634
  function stageParaPatch(scan, para, newPlain, skip) {
23569
23635
  if (!scan.safe) return skip("\uC139\uC158 \uB808\uCF54\uB4DC \uC7AC\uC9C1\uB82C\uD654 \uBD88\uC77C\uCE58 \u2014 \uC548\uC804\uC744 \uC704\uD574 \uC774 \uC139\uC158\uC740 \uBBF8\uC9C0\uC6D0");
23570
- if (para.textIdx === -1) return skip("\uBE48 \uBB38\uB2E8 \uD14D\uC2A4\uD2B8 \uCD94\uAC00\uB294 \uBBF8\uC9C0\uC6D0 (v1)");
23571
23636
  if (para.textIdx === -2) return skip("\uBCF5\uC218 PARA_TEXT \uB808\uCF54\uB4DC \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
23572
23637
  if (para.rangeTagCount > 0) return skip("\uBC94\uC704 \uD0DC\uADF8(\uD615\uAD11\uD39C/\uAD50\uC815\uBD80\uD638) \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
23573
23638
  if (para.charShapeIdx < 0 || para.lineSegIdx < 0) return skip("\uBB38\uB2E8 \uB808\uCF54\uB4DC \uAD6C\uC131 \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
@@ -23575,11 +23640,27 @@ function stageParaPatch(scan, para, newPlain, skip) {
23575
23640
  if (/[\u0000-\u001f]/.test(newPlain)) return skip("\uC0C8 \uD14D\uC2A4\uD2B8\uC5D0 \uC81C\uC5B4\uBB38\uC790 \uD3EC\uD568 \u2014 \uBBF8\uC9C0\uC6D0");
23576
23641
  const records = scan.records;
23577
23642
  const headerRec = records[para.headerIdx];
23578
- const textRec = records[para.textIdx];
23579
23643
  const charShapeRec = records[para.charShapeIdx];
23580
23644
  if (charShapeRec.data.length < 8) {
23581
23645
  return skip("CHAR_SHAPE \uB808\uCF54\uB4DC \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
23582
23646
  }
23647
+ if (para.textIdx === -1) {
23648
+ const nCharsLow = para.nCharsRaw & 2147483647;
23649
+ if (nCharsLow > 1) return skip("PARA_TEXT \uC5C6\uB294 \uBB38\uB2E8\uC758 nChars \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
23650
+ const paraEnd = nCharsLow === 1 ? Buffer.from([13, 0]) : Buffer.alloc(0);
23651
+ const at = para.headerIdx + 1;
23652
+ const list = scan.inserts.get(at) ?? [];
23653
+ list.push({ tagId: TAG_PARA_TEXT, level: headerRec.level + 1, data: Buffer.concat([Buffer.from(newPlain, "utf16le"), paraEnd]) });
23654
+ scan.inserts.set(at, list);
23655
+ const newHeader2 = Buffer.from(headerRec.data);
23656
+ newHeader2.writeUInt32LE((para.nCharsRaw & 2147483648 | newPlain.length + nCharsLow) >>> 0, 0);
23657
+ const cs2 = rebuildCharShape(charShapeRec.data, 0);
23658
+ scan.repl.set(para.charShapeIdx, cs2.buf);
23659
+ newHeader2.writeUInt16LE(cs2.count, 12);
23660
+ scan.repl.set(para.headerIdx, newHeader2);
23661
+ return 1;
23662
+ }
23663
+ const textRec = records[para.textIdx];
23583
23664
  const seg = splitParaText(textRec.data);
23584
23665
  if (!seg) {
23585
23666
  return skip(para.ctrlMask !== 0 ? "\uCEE8\uD2B8\uB864 \uBB38\uC790(\uD0ED/\uD544\uB4DC/\uD2B9\uC218\uACF5\uBC31 \uB4F1 \uD14D\uC2A4\uD2B8 \uC911\uAC04) \uD3EC\uD568 \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)" : "PARA_TEXT \uC7AC\uAD6C\uC131 \uBD88\uC77C\uCE58 \u2014 \uC6D0\uBB38 \uBCF4\uC874 \uBD88\uAC00\uB85C \uBBF8\uC9C0\uC6D0");
@@ -24185,7 +24266,7 @@ async function parseHwp(buffer, options) {
24185
24266
  async function parsePdf(buffer, options) {
24186
24267
  let parsePdfDocument;
24187
24268
  try {
24188
- const mod = await import("./parser-DR5CTZ74.js");
24269
+ const mod = await import("./parser-7G5F7PT2.js");
24189
24270
  parsePdfDocument = mod.parsePdfDocument;
24190
24271
  } catch {
24191
24272
  return {
@@ -24320,4 +24401,4 @@ export {
24320
24401
  parseHwpml,
24321
24402
  fillForm
24322
24403
  };
24323
- //# sourceMappingURL=chunk-NXRABCWW.js.map
24404
+ //# sourceMappingURL=chunk-SLKF72QF.js.map