formatferry 1.0.16 → 1.0.18

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 (2) hide show
  1. package/dist/index.js +55 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22126,6 +22126,30 @@ function escapeHtml(str) {
22126
22126
  function computeFontSize(transform) {
22127
22127
  return Math.sqrt(transform[2] ** 2 + transform[3] ** 2);
22128
22128
  }
22129
+ var CJK_REGEX2 = /[\u4E00-\u9FFF\u3400-\u4DBF\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF\uF900-\uFAFF\uFF00-\uFF60\uFFE0-\uFFEF]/;
22130
+ function containsCJK(str) {
22131
+ return CJK_REGEX2.test(str);
22132
+ }
22133
+ function isCJKChar(ch) {
22134
+ const cp = ch.codePointAt(0);
22135
+ return cp >= 19968 && cp <= 40959 || // CJK Unified Ideographs
22136
+ cp >= 13312 && cp <= 19903 || // CJK Extension A
22137
+ cp >= 12288 && cp <= 12351 || // CJK Symbols and Punctuation
22138
+ cp >= 12352 && cp <= 12447 || // Hiragana
22139
+ cp >= 12448 && cp <= 12543 || // Katakana
22140
+ cp >= 44032 && cp <= 55215 || // Hangul Syllables
22141
+ cp >= 63744 && cp <= 64255 || // CJK Compatibility Ideographs
22142
+ // Fullwidth Forms — exclude halfwidth Katakana (FF61-FF9F) which are narrow
22143
+ cp >= 65280 && cp <= 65376 || // Fullwidth Latin + punctuation
22144
+ cp >= 65504 && cp <= 65519;
22145
+ }
22146
+ function visualCharCount(str) {
22147
+ let count = 0;
22148
+ for (const ch of str) {
22149
+ count += isCJKChar(ch) ? 2 : 1;
22150
+ }
22151
+ return count;
22152
+ }
22129
22153
  function normalizeLigatures(text) {
22130
22154
  return text.normalize("NFC").replace(/ff/g, "ff").replace(/fi/g, "fi").replace(/fl/g, "fl").replace(/ffi/g, "ffi").replace(/ffl/g, "ffl");
22131
22155
  }
@@ -22137,8 +22161,8 @@ function isSafeUrl(url) {
22137
22161
  }
22138
22162
  function detectFontStyle(fontName) {
22139
22163
  if (!fontName) return null;
22140
- const bold = /(Bold|Black|Heavy|Semibold|Demi)/i.test(fontName) && !/Blackletter/i.test(fontName);
22141
- const italic = /(Italic|Oblique|Slanted)/i.test(fontName);
22164
+ const bold = /(Bold|Black|Heavy|Semibold|Demi|太|中太)/i.test(fontName) && !/Blackletter/i.test(fontName);
22165
+ const italic = /(Italic|Oblique|Slanted|Kaiti|楷体|斜)/i.test(fontName);
22142
22166
  if (bold && italic) return "bold-italic";
22143
22167
  if (bold) return "bold";
22144
22168
  if (italic) return "italic";
@@ -22188,7 +22212,7 @@ function textItemToPositioned(item) {
22188
22212
  const ty = item.transform[5];
22189
22213
  const rawHeight = item.height ?? Math.abs(item.transform[3]) ?? 10;
22190
22214
  const height = rawHeight > 0 ? rawHeight : 10;
22191
- const width = item.width ?? item.str.length * (height * 0.5);
22215
+ const width = item.width ?? visualCharCount(item.str) * (height * 0.5);
22192
22216
  const descent = height * DESCENDER_BUDGET_FACTOR;
22193
22217
  const y = ty - descent;
22194
22218
  const paddedHeight = height + descent;
@@ -22276,7 +22300,10 @@ function itemsToLinearText(items) {
22276
22300
  rows.push([it]);
22277
22301
  }
22278
22302
  }
22279
- const widths = items.map((i) => i.str.length > 0 ? i.width / i.str.length : 0).filter((w) => w > 0);
22303
+ const widths = items.map((i) => {
22304
+ const vc = visualCharCount(i.str);
22305
+ return vc > 0 ? i.width / vc : 0;
22306
+ }).filter((w) => w > 0);
22280
22307
  const medianCharWidth = widths.length ? [...widths].sort((a, b) => a - b)[Math.floor(widths.length / 2)] || 1 : 1;
22281
22308
  const labelGap = medianCharWidth * FORM_LABEL_GAP_FACTOR;
22282
22309
  const NBSP2 = "\xA0\xA0";
@@ -22339,7 +22366,10 @@ function renderTextBlockEnhanced(items, medianPageFontSize, outlineTitles) {
22339
22366
  row.sort((a, b) => a.x - b.x);
22340
22367
  const fsSamples = row.map((it) => it.fontSize ?? 0).filter((f) => f > 0);
22341
22368
  const rowFs = fsSamples.length > 0 ? [...fsSamples].sort((a, b) => a - b)[Math.floor(fsSamples.length / 2)] : medianPageFontSize;
22342
- const rowWidths = row.map((it) => it.str.length > 0 ? it.width / it.str.length : 0).filter((w) => w > 0);
22369
+ const rowWidths = row.map((it) => {
22370
+ const vc = visualCharCount(it.str);
22371
+ return vc > 0 ? it.width / vc : 0;
22372
+ }).filter((w) => w > 0);
22343
22373
  const rowMedianCharWidth = rowWidths.length ? [...rowWidths].sort((a, b) => a - b)[Math.floor(rowWidths.length / 2)] || 1 : 1;
22344
22374
  const rowLabelGap = rowMedianCharWidth * FORM_LABEL_GAP_FACTOR;
22345
22375
  let plainText = "";
@@ -22422,8 +22452,14 @@ function repairRowHyphenation(rows) {
22422
22452
  const nextRow = rows[i + 1];
22423
22453
  const lastItem = [...row].sort((a, b) => b.right - a.right)[0];
22424
22454
  const firstNext = [...nextRow].sort((a, b) => a.x - b.x)[0];
22425
- if (lastItem && firstNext && /-$/.test(lastItem.str) && new RegExp("^\\p{Ll}", "u").test(firstNext.str)) {
22426
- const fixed = { ...lastItem, str: lastItem.str.slice(0, -1) };
22455
+ if (lastItem && firstNext && /-$/.test(lastItem.str) && !containsCJK(lastItem.str) && new RegExp("^\\p{Ll}", "u").test(firstNext.str)) {
22456
+ const hyphenWidth = lastItem.width / lastItem.str.length * 0.4;
22457
+ const fixed = {
22458
+ ...lastItem,
22459
+ str: lastItem.str.slice(0, -1),
22460
+ width: lastItem.width - hyphenWidth,
22461
+ right: lastItem.right - hyphenWidth
22462
+ };
22427
22463
  result.push([...row.map((it) => it === lastItem ? fixed : it), ...nextRow]);
22428
22464
  i += 2;
22429
22465
  continue;
@@ -22435,8 +22471,12 @@ function repairRowHyphenation(rows) {
22435
22471
  return result;
22436
22472
  }
22437
22473
  function detectListMarker(plainText) {
22474
+ const cjkBulletMatch = /^[・〇☆※]\s*(.+)/u.exec(plainText);
22475
+ if (cjkBulletMatch && cjkBulletMatch[1].length > 0) return { kind: "ul", bodyText: cjkBulletMatch[1] };
22438
22476
  const bulletMatch = /^[•◦◆▪▸–\-*·]\s+(.+)/u.exec(plainText);
22439
22477
  if (bulletMatch) return { kind: "ul", bodyText: bulletMatch[1] };
22478
+ const cjkNumberedMatch = /^[\u2460-\u24FF\u3248-\u325F\u3200-\u321E㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉]\s*(.+)/u.exec(plainText);
22479
+ if (cjkNumberedMatch) return { kind: "ol", bodyText: cjkNumberedMatch[1] };
22440
22480
  const numberedMatch = /^\d+[.)]\s+(.+)/.exec(plainText);
22441
22481
  if (numberedMatch) return { kind: "ol", bodyText: numberedMatch[1] };
22442
22482
  const letteredMatch = /^[a-z][.)]\s+(.+)/.exec(plainText);
@@ -22547,7 +22587,6 @@ function extractVectorLines(fnArray, argsArray, pageWidth, pageHeight) {
22547
22587
  let pathStartX = 0;
22548
22588
  let pathStartY = 0;
22549
22589
  let pending = [];
22550
- const isPathOp = (op) => op === PDF_OP.moveTo || op === PDF_OP.lineTo || op === PDF_OP.closePath || op === PDF_OP.rectangle;
22551
22590
  const isStrokeOp = (op) => op === PDF_OP.stroke || op === PDF_OP.closeStroke || op === PDF_OP.fillStroke;
22552
22591
  const flushPending = () => {
22553
22592
  for (const p of pending) {
@@ -22573,6 +22612,7 @@ function extractVectorLines(fnArray, argsArray, pageWidth, pageHeight) {
22573
22612
  const op = fnArray[i];
22574
22613
  const args = argsArray[i] ?? [];
22575
22614
  if (op === PDF_OP.moveTo) {
22615
+ pending = [];
22576
22616
  curX = args[0] ?? 0;
22577
22617
  curY = args[1] ?? 0;
22578
22618
  pathStartX = curX;
@@ -22598,8 +22638,6 @@ function extractVectorLines(fnArray, argsArray, pageWidth, pageHeight) {
22598
22638
  curY = pathStartY;
22599
22639
  } else if (isStrokeOp(op)) {
22600
22640
  flushPending();
22601
- } else if (!isPathOp(op)) {
22602
- pending = [];
22603
22641
  }
22604
22642
  }
22605
22643
  return lines;
@@ -24270,7 +24308,7 @@ function extractArticle(html, domProvider) {
24270
24308
  }
24271
24309
 
24272
24310
  // ../../src/shared/token-estimator.ts
24273
- var CJK_REGEX2 = /[\u4e00-\u9fff\u3400-\u4dbf\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g;
24311
+ var CJK_REGEX3 = /[\u4e00-\u9fff\u3400-\u4dbf\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g;
24274
24312
  var MAX_INPUT_LENGTH = 1e6;
24275
24313
  function estimateTokens(text) {
24276
24314
  if (!text || text.length === 0) {
@@ -24279,7 +24317,7 @@ function estimateTokens(text) {
24279
24317
  if (text.length > MAX_INPUT_LENGTH) {
24280
24318
  return Math.round(text.length / 4);
24281
24319
  }
24282
- const cjkMatches = text.match(CJK_REGEX2);
24320
+ const cjkMatches = text.match(CJK_REGEX3);
24283
24321
  const cjkCount = cjkMatches ? cjkMatches.length : 0;
24284
24322
  const otherCount = text.length - cjkCount;
24285
24323
  const cjkTokens = cjkCount / 1.5;
@@ -24639,6 +24677,9 @@ var ToonifyService = class {
24639
24677
  paragraphLines.push(line);
24640
24678
  endIndex++;
24641
24679
  }
24680
+ if (paragraphLines.length === 0) {
24681
+ return { block: null, endIndex: startIndex };
24682
+ }
24642
24683
  return {
24643
24684
  block: { p: paragraphLines.join(" ").trim() },
24644
24685
  endIndex: endIndex - 1
@@ -25317,8 +25358,8 @@ import { readFileSync as readFileSync4 } from "fs";
25317
25358
  import { resolve, dirname as dirname3 } from "path";
25318
25359
  import { fileURLToPath } from "url";
25319
25360
  var getVersion = () => {
25320
- if ("1.0.16") {
25321
- return "1.0.16";
25361
+ if ("1.0.18") {
25362
+ return "1.0.18";
25322
25363
  }
25323
25364
  try {
25324
25365
  const __dirname = dirname3(fileURLToPath(import.meta.url));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formatferry",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "FormatFerry CLI — Convert HTML, DOCX, PDF, XLSX, CSV, JSON, XML, PPTX to Markdown",
5
5
  "keywords": [
6
6
  "markdown",