formatferry 1.0.24 → 1.0.26

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 +365 -50
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,9 +5,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __commonJS = (cb, mod) => function __require() {
9
12
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
13
  };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
11
18
  var __copyProps = (to, from, except, desc) => {
12
19
  if (from && typeof from === "object" || typeof from === "function") {
13
20
  for (let key of __getOwnPropNames(from))
@@ -2795,12 +2802,12 @@ var require_ChildNode = __commonJS({
2795
2802
  "use strict";
2796
2803
  var Node2 = require_Node();
2797
2804
  var LinkedList = require_LinkedList();
2798
- var createDocumentFragmentFromArguments = function(document2, args) {
2799
- var docFrag = document2.createDocumentFragment();
2805
+ var createDocumentFragmentFromArguments = function(document3, args) {
2806
+ var docFrag = document3.createDocumentFragment();
2800
2807
  for (var i = 0; i < args.length; i++) {
2801
2808
  var argItem = args[i];
2802
2809
  var isNode = argItem instanceof Node2;
2803
- docFrag.appendChild(isNode ? argItem : document2.createTextNode(String(argItem)));
2810
+ docFrag.appendChild(isNode ? argItem : document3.createTextNode(String(argItem)));
2804
2811
  }
2805
2812
  return docFrag;
2806
2813
  };
@@ -3068,7 +3075,7 @@ var require_Element = __commonJS({
3068
3075
  return NodeUtils.serializeOne(this, { nodeType: 0 });
3069
3076
  },
3070
3077
  set: function(v) {
3071
- var document2 = this.ownerDocument;
3078
+ var document3 = this.ownerDocument;
3072
3079
  var parent = this.parentNode;
3073
3080
  if (parent === null) {
3074
3081
  return;
@@ -3079,8 +3086,8 @@ var require_Element = __commonJS({
3079
3086
  if (parent.nodeType === Node2.DOCUMENT_FRAGMENT_NODE) {
3080
3087
  parent = parent.ownerDocument.createElement("body");
3081
3088
  }
3082
- var parser = document2.implementation.mozHTMLParser(
3083
- document2._address,
3089
+ var parser = document3.implementation.mozHTMLParser(
3090
+ document3._address,
3084
3091
  parent
3085
3092
  );
3086
3093
  parser.parse(v === null ? "" : String(v), true);
@@ -5665,9 +5672,9 @@ var require_defineElement = __commonJS({
5665
5672
  });
5666
5673
  return c;
5667
5674
  };
5668
- function EventHandlerBuilder(body, document2, form, element) {
5675
+ function EventHandlerBuilder(body, document3, form, element) {
5669
5676
  this.body = body;
5670
- this.document = document2;
5677
+ this.document = document3;
5671
5678
  this.form = form;
5672
5679
  this.element = element;
5673
5680
  }
@@ -16542,8 +16549,8 @@ var require_Window = __commonJS({
16542
16549
  var Location = require_Location();
16543
16550
  var utils = require_utils();
16544
16551
  module.exports = Window;
16545
- function Window(document2) {
16546
- this.document = document2 || new DOMImplementation(null).createHTMLDocument("");
16552
+ function Window(document3) {
16553
+ this.document = document3 || new DOMImplementation(null).createHTMLDocument("");
16547
16554
  this.document._scripting_enabled = true;
16548
16555
  this.document.defaultView = this;
16549
16556
  this.location = new Location(this, this.document._address || "about:blank");
@@ -16674,11 +16681,11 @@ var require_lib = __commonJS({
16674
16681
  };
16675
16682
  };
16676
16683
  exports.createWindow = function(html, address) {
16677
- var document2 = exports.createDocument(html);
16684
+ var document3 = exports.createDocument(html);
16678
16685
  if (address !== void 0) {
16679
- document2._address = address;
16686
+ document3._address = address;
16680
16687
  }
16681
- return new impl.Window(document2);
16688
+ return new impl.Window(document3);
16682
16689
  };
16683
16690
  exports.impl = impl;
16684
16691
  }
@@ -17409,6 +17416,137 @@ var require_turndown_cjs = __commonJS({
17409
17416
  }
17410
17417
  });
17411
17418
 
17419
+ // ../../src/shared/feature-flags.ts
17420
+ function getFeatureFlags() {
17421
+ return { ..._flags };
17422
+ }
17423
+ var _flags;
17424
+ var init_feature_flags = __esm({
17425
+ "../../src/shared/feature-flags.ts"() {
17426
+ _flags = {
17427
+ enableDocx: true,
17428
+ enableXlsx: true,
17429
+ enablePdf: true,
17430
+ enableHtml: true,
17431
+ enablePdfTableDetection: true,
17432
+ enableData: true,
17433
+ enablePptx: true,
17434
+ enablePdfEnhanced: true,
17435
+ enablePdfMultiColumn: true,
17436
+ enablePdfOcr: false
17437
+ };
17438
+ }
17439
+ });
17440
+
17441
+ // ../../src/shared/adapters/pdf-ocr-adapter.ts
17442
+ var pdf_ocr_adapter_exports = {};
17443
+ __export(pdf_ocr_adapter_exports, {
17444
+ isOcrAvailable: () => isOcrAvailable,
17445
+ runOcr: () => runOcr
17446
+ });
17447
+ async function loadTesseract() {
17448
+ try {
17449
+ const mod = await import("tesseract.js");
17450
+ if (mod && typeof mod.createWorker === "function") {
17451
+ return mod;
17452
+ }
17453
+ return null;
17454
+ } catch {
17455
+ return null;
17456
+ }
17457
+ }
17458
+ function preScaleCanvas(canvas) {
17459
+ if (canvas.width <= MAX_IMAGE_WIDTH) return canvas;
17460
+ const scale = MAX_IMAGE_WIDTH / canvas.width;
17461
+ const scaled = document.createElement("canvas");
17462
+ scaled.width = MAX_IMAGE_WIDTH;
17463
+ scaled.height = Math.round(canvas.height * scale);
17464
+ const ctx = scaled.getContext("2d");
17465
+ if (ctx) {
17466
+ ctx.drawImage(canvas, 0, 0, scaled.width, scaled.height);
17467
+ }
17468
+ return scaled;
17469
+ }
17470
+ function canvasToBlob(canvas) {
17471
+ return new Promise((resolve2, reject) => {
17472
+ canvas.toBlob(
17473
+ (blob) => {
17474
+ if (blob) resolve2(blob);
17475
+ else reject(new Error("canvas.toBlob returned null"));
17476
+ },
17477
+ "image/png"
17478
+ );
17479
+ });
17480
+ }
17481
+ async function isOcrAvailable() {
17482
+ if (!getFeatureFlags().enablePdfOcr) return false;
17483
+ const lib = await loadTesseract();
17484
+ return lib !== null;
17485
+ }
17486
+ async function runOcr(pageCanvases, options) {
17487
+ const lib = await loadTesseract();
17488
+ if (!lib) {
17489
+ throw new Error(
17490
+ "OCR is not available: tesseract.js could not be loaded. Ensure the tesseract.js package is installed."
17491
+ );
17492
+ }
17493
+ const warnings = [];
17494
+ const pages = [];
17495
+ const worker = await lib.createWorker("eng", 1, {
17496
+ logger: (m) => {
17497
+ if (options?.onProgress) {
17498
+ options.onProgress({
17499
+ progress: m.progress,
17500
+ status: m.status
17501
+ });
17502
+ }
17503
+ }
17504
+ });
17505
+ try {
17506
+ for (let i = 0; i < pageCanvases.length; i++) {
17507
+ const { pageNumber, canvas } = pageCanvases[i];
17508
+ const scaled = preScaleCanvas(canvas);
17509
+ const blob = await canvasToBlob(scaled);
17510
+ try {
17511
+ const { data } = await worker.recognize(blob);
17512
+ const text = data.text.trim();
17513
+ if (data.confidence < MIN_CONFIDENCE) {
17514
+ warnings.push(
17515
+ `Page ${pageNumber}: low OCR confidence (${Math.round(data.confidence)}%). Text quality may be poor.`
17516
+ );
17517
+ }
17518
+ pages.push({
17519
+ pageNumber,
17520
+ text: text || "",
17521
+ confidence: data.confidence
17522
+ });
17523
+ } catch (err) {
17524
+ warnings.push(
17525
+ `Page ${pageNumber}: OCR failed \u2014 ${err instanceof Error ? err.message : String(err)}`
17526
+ );
17527
+ pages.push({ pageNumber, text: "", confidence: 0 });
17528
+ }
17529
+ if (options?.onProgress) {
17530
+ options.onProgress({
17531
+ progress: (i + 1) / pageCanvases.length,
17532
+ status: `OCR page ${i + 1}/${pageCanvases.length}`
17533
+ });
17534
+ }
17535
+ }
17536
+ } finally {
17537
+ await worker.terminate();
17538
+ }
17539
+ return { pages, warnings };
17540
+ }
17541
+ var MAX_IMAGE_WIDTH, MIN_CONFIDENCE;
17542
+ var init_pdf_ocr_adapter = __esm({
17543
+ "../../src/shared/adapters/pdf-ocr-adapter.ts"() {
17544
+ init_feature_flags();
17545
+ MAX_IMAGE_WIDTH = 2e3;
17546
+ MIN_CONFIDENCE = 40;
17547
+ }
17548
+ });
17549
+
17412
17550
  // ../../node_modules/@mozilla/readability/Readability.js
17413
17551
  var require_Readability = __commonJS({
17414
17552
  "../../node_modules/@mozilla/readability/Readability.js"(exports, module) {
@@ -19471,9 +19609,9 @@ var require_readability = __commonJS({
19471
19609
  // src/shim.ts
19472
19610
  import { parseHTML } from "linkedom";
19473
19611
  import createDOMPurify from "dompurify";
19474
- var { document, window: window2 } = parseHTML("<!DOCTYPE html><html><body></body></html>");
19612
+ var { document: document2, window: window2 } = parseHTML("<!DOCTYPE html><html><body></body></html>");
19475
19613
  globalThis.window = window2;
19476
- globalThis.document = document;
19614
+ globalThis.document = document2;
19477
19615
  globalThis.DOMParser = window2.DOMParser;
19478
19616
  globalThis.Node = window2.Node;
19479
19617
  globalThis.HTMLElement = window2.HTMLElement;
@@ -21449,21 +21587,8 @@ var DocxAdapter = class {
21449
21587
  };
21450
21588
  var docxAdapter = new DocxAdapter();
21451
21589
 
21452
- // ../../src/shared/feature-flags.ts
21453
- var _flags = {
21454
- enableDocx: true,
21455
- enableXlsx: true,
21456
- enablePdf: true,
21457
- enableHtml: true,
21458
- enablePdfTableDetection: true,
21459
- enableData: true,
21460
- enablePptx: true,
21461
- enablePdfEnhanced: true,
21462
- enablePdfMultiColumn: true
21463
- };
21464
- function getFeatureFlags() {
21465
- return { ..._flags };
21466
- }
21590
+ // ../../src/shared/adapters/pdf-adapter.ts
21591
+ init_feature_flags();
21467
21592
 
21468
21593
  // ../../src/shared/adapters/pdf-table-detector.ts
21469
21594
  var CJK_REGEX = /[\u3400-\u9FFF\uF900-\uFAFF\u3040-\u30FF\uAC00-\uD7AF]/;
@@ -22424,6 +22549,58 @@ function dedupAgainstWidgets(textItems, widgets) {
22424
22549
  }
22425
22550
  return kept;
22426
22551
  }
22552
+ function associateLabelsToWidgets(widgets, textItems) {
22553
+ if (widgets.length === 0 || textItems.length === 0) return [widgets, textItems];
22554
+ const consumedIndices = /* @__PURE__ */ new Set();
22555
+ const updatedWidgets = widgets.map((w) => {
22556
+ if (/^\[([ X]|Signed|Signature)\]$/.test(w.str)) return w;
22557
+ const refSize = Math.max(w.height, 10);
22558
+ const leftTolerance = refSize * 3;
22559
+ const aboveTolerance = refSize * 1;
22560
+ let bestCandidate = null;
22561
+ for (let i = 0; i < textItems.length; i++) {
22562
+ if (consumedIndices.has(i)) continue;
22563
+ const txt = textItems[i];
22564
+ const isToLeft = txt.right <= w.x + 2;
22565
+ if (isToLeft) {
22566
+ const hGap = w.x - txt.right;
22567
+ if (hGap <= leftTolerance) {
22568
+ const yOverlap = Math.min(txt.y + txt.height, w.y + w.height) - Math.max(txt.y, w.y);
22569
+ const minH = Math.min(txt.height, w.height);
22570
+ if (yOverlap / minH >= 0.5) {
22571
+ const score = hGap;
22572
+ if (!bestCandidate || score < bestCandidate.score) {
22573
+ bestCandidate = { idx: i, str: txt.str, score };
22574
+ }
22575
+ }
22576
+ }
22577
+ }
22578
+ if (!isToLeft) {
22579
+ const widgetTop = w.y + w.height;
22580
+ const labelBottom = txt.y;
22581
+ const vGap = labelBottom - widgetTop;
22582
+ if (vGap >= 0 && vGap <= aboveTolerance) {
22583
+ const xOverlap = Math.min(txt.right, w.right) - Math.max(txt.x, w.x);
22584
+ if (xOverlap > 0) {
22585
+ const score = vGap;
22586
+ if (!bestCandidate || score < bestCandidate.score) {
22587
+ bestCandidate = { idx: i, str: txt.str, score };
22588
+ }
22589
+ }
22590
+ }
22591
+ }
22592
+ }
22593
+ if (bestCandidate) {
22594
+ consumedIndices.add(bestCandidate.idx);
22595
+ const labelStr = bestCandidate.str.trimEnd();
22596
+ const separator = /[::]\s*$/.test(labelStr) ? " " : ": ";
22597
+ return { ...w, str: `${labelStr}${separator}${w.str}` };
22598
+ }
22599
+ return w;
22600
+ });
22601
+ const remainingText = textItems.filter((_, i) => !consumedIndices.has(i));
22602
+ return [updatedWidgets, remainingText];
22603
+ }
22427
22604
  function itemsToLinearText(items) {
22428
22605
  if (items.length === 0) return "";
22429
22606
  const sorted = [...items].sort((a, b) => b.y + b.height - (a.y + a.height) || a.x - b.x);
@@ -23044,8 +23221,9 @@ var PdfAdapter = class {
23044
23221
  onProgress?.(pageNum / numPages);
23045
23222
  const page = await pdf.getPage(pageNum);
23046
23223
  const viewport = page.getViewport({ scale: 1 });
23224
+ const useMarkedContent = getFeatureFlags().enablePdfEnhanced && isTaggedPdf;
23047
23225
  const [textContent, annotations] = await Promise.all([
23048
- page.getTextContent(),
23226
+ page.getTextContent(useMarkedContent ? { includeMarkedContent: true } : void 0),
23049
23227
  page.getAnnotations().catch((err) => {
23050
23228
  console.warn(`[PdfAdapter] getAnnotations failed for page ${pageNum}:`, err);
23051
23229
  return [];
@@ -23053,10 +23231,23 @@ var PdfAdapter = class {
23053
23231
  ]);
23054
23232
  const positionedText = [];
23055
23233
  const xPositions = [];
23234
+ let currentMcid;
23056
23235
  for (const item of textContent.items) {
23236
+ const itemType = "type" in item ? item.type : void 0;
23237
+ if (itemType === "beginMarkedContentProps") {
23238
+ currentMcid = item.id?.toString();
23239
+ continue;
23240
+ }
23241
+ if (itemType === "endMarkedContent") {
23242
+ currentMcid = void 0;
23243
+ continue;
23244
+ }
23057
23245
  if (!("str" in item) || typeof item.str !== "string") continue;
23058
23246
  const positioned = textItemToPositioned(item);
23059
23247
  if (!positioned) continue;
23248
+ if (currentMcid !== void 0) {
23249
+ positioned.mcid = currentMcid;
23250
+ }
23060
23251
  positionedText.push(positioned);
23061
23252
  xPositions.push(positioned.x);
23062
23253
  }
@@ -23064,45 +23255,104 @@ var PdfAdapter = class {
23064
23255
  ...annotations.flatMap(widgetToPositionedItems),
23065
23256
  ...groupRadioButtons(annotations)
23066
23257
  ];
23067
- const items = widgetItems.length > 0 ? [...dedupAgainstWidgets(positionedText, widgetItems), ...widgetItems] : positionedText;
23258
+ let items;
23259
+ if (widgetItems.length > 0) {
23260
+ const dedupedText = dedupAgainstWidgets(positionedText, widgetItems);
23261
+ const [labelledWidgets, remainingText] = associateLabelsToWidgets(widgetItems, dedupedText);
23262
+ items = [...remainingText, ...labelledWidgets];
23263
+ } else {
23264
+ items = positionedText;
23265
+ }
23068
23266
  if (getFeatureFlags().enablePdfEnhanced) {
23069
23267
  applyLinkAnnotations(items, annotations);
23070
23268
  }
23071
23269
  const renderItems = getFeatureFlags().enablePdfEnhanced && hfSuppressSet.size > 0 ? items.filter((it) => !isHeaderFooterItem(it, hfSuppressSet)) : items;
23072
23270
  if (getFeatureFlags().enablePdfEnhanced && isTaggedPdf) {
23073
23271
  let structHtml = null;
23272
+ let claimedMcids = null;
23074
23273
  try {
23075
23274
  if (hasStructTree(page)) {
23076
23275
  const structResult = await structTreeToHtml(page);
23077
23276
  if (structResult !== null) {
23078
23277
  structHtml = structResult.html;
23278
+ claimedMcids = structResult.claimedMcids;
23079
23279
  }
23080
23280
  }
23081
23281
  } catch {
23082
23282
  }
23083
- if (structHtml !== null) {
23084
- const structTextLen = structHtml.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim().length;
23085
- const geoTextLen = renderItems.map((it) => it.str).join(" ").length;
23086
- if (geoTextLen > 0 && structTextLen / geoTextLen < 0.7) {
23087
- structHtml = null;
23283
+ let fusionStructHtml = null;
23284
+ let fusionClaimedMcids = null;
23285
+ if (structHtml !== null && claimedMcids !== null) {
23286
+ const textItems = renderItems.filter((it) => it.source !== "acroform");
23287
+ const covered = textItems.filter((it) => it.mcid != null && claimedMcids.has(it.mcid)).length;
23288
+ const coverage = covered / Math.max(textItems.length, 1);
23289
+ if (coverage >= 0.7) {
23290
+ const plainText = structHtml.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
23291
+ totalTextLength += plainText.length;
23292
+ let hasImages2 = false;
23293
+ try {
23294
+ const opList = await page.getOperatorList();
23295
+ hasImages2 = opList.fnArray.some((op) => op === 85 || op === 86);
23296
+ } catch {
23297
+ }
23298
+ pages.push({
23299
+ pageNumber: pageNum,
23300
+ textContent: plainText,
23301
+ hasImages: hasImages2,
23302
+ viewportWidth: viewport.width,
23303
+ htmlBlocks: [structHtml],
23304
+ tablesDetected: 0
23305
+ });
23306
+ continue;
23307
+ } else if (coverage > 0) {
23308
+ fusionStructHtml = structHtml;
23309
+ fusionClaimedMcids = claimedMcids;
23088
23310
  }
23089
23311
  }
23090
- if (structHtml !== null) {
23091
- const plainText = structHtml.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
23092
- totalTextLength += plainText.length;
23093
- let hasImages2 = false;
23312
+ if (fusionStructHtml !== null && fusionClaimedMcids !== null) {
23313
+ const unclaimedItems = renderItems.filter(
23314
+ (it) => !it.mcid || !fusionClaimedMcids.has(it.mcid)
23315
+ );
23316
+ const fsSamples = unclaimedItems.map((it) => it.fontSize ?? 0).filter((f) => f > 0);
23317
+ const medianFontSize = fsSamples.length > 0 ? [...fsSamples].sort((a, b) => a - b)[Math.floor(fsSamples.length / 2)] : 12;
23318
+ const fusionEnhancedOpts = getFeatureFlags().enablePdfEnhanced ? { medianFontSize, outlineTitles } : void 0;
23319
+ const pageBottomY2 = Array.isArray(page.view) && page.view.length >= 2 ? page.view[1] : 0;
23320
+ let pageOpList2;
23321
+ let ruleSeparators2;
23094
23322
  try {
23095
- const opList = await page.getOperatorList();
23096
- hasImages2 = opList.fnArray.some((op) => op === 85 || op === 86);
23323
+ pageOpList2 = await page.getOperatorList();
23324
+ if (getFeatureFlags().enablePdfEnhanced && tableDetectionEnabled) {
23325
+ ruleSeparators2 = extractVectorLines(
23326
+ pageOpList2.fnArray,
23327
+ pageOpList2.argsArray ?? [],
23328
+ viewport.width,
23329
+ viewport.height
23330
+ );
23331
+ }
23097
23332
  } catch {
23098
23333
  }
23334
+ const rendered2 = renderPage(
23335
+ unclaimedItems,
23336
+ pageNum,
23337
+ pageNum < numPages ? pageNum + 1 : void 0,
23338
+ tableDetectionEnabled,
23339
+ translate,
23340
+ pageBottomY2,
23341
+ fusionEnhancedOpts,
23342
+ ruleSeparators2
23343
+ );
23344
+ for (const w of rendered2.warnings) warnings.push(w);
23345
+ const structPlainText = fusionStructHtml.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
23346
+ totalTextLength += structPlainText.length + rendered2.textContent.length;
23347
+ const hasImages2 = pageOpList2?.fnArray.some((op) => op === 85 || op === 86) ?? false;
23348
+ const mergedHtmlBlocks = [fusionStructHtml, ...rendered2.htmlBlocks];
23099
23349
  pages.push({
23100
23350
  pageNumber: pageNum,
23101
- textContent: plainText,
23351
+ textContent: structPlainText + "\n" + rendered2.textContent,
23102
23352
  hasImages: hasImages2,
23103
23353
  viewportWidth: viewport.width,
23104
- htmlBlocks: [structHtml],
23105
- tablesDetected: 0
23354
+ htmlBlocks: mergedHtmlBlocks,
23355
+ tablesDetected: rendered2.tablesDetected
23106
23356
  });
23107
23357
  continue;
23108
23358
  }
@@ -23174,6 +23424,12 @@ var PdfAdapter = class {
23174
23424
  const hasNoText = totalTextLength === 0;
23175
23425
  const hasMinimalTextWithImages = totalTextLength < MIN_TEXT_CHARS_THRESHOLD && hasAnyImages;
23176
23426
  if (hasNoText || hasMinimalTextWithImages) {
23427
+ if (getFeatureFlags().enablePdfOcr) {
23428
+ const ocrResult = await this.runOcrOnPages(pdf, numPages);
23429
+ if (ocrResult !== null) {
23430
+ return ocrResult;
23431
+ }
23432
+ }
23177
23433
  throw new PdfParseError(
23178
23434
  "PDF contains only images \u2014 no text layer available.",
23179
23435
  "IMAGE_ONLY"
@@ -23279,6 +23535,65 @@ var PdfAdapter = class {
23279
23535
  }
23280
23536
  }
23281
23537
  }
23538
+ /**
23539
+ * Attempt OCR extraction on all pages of an image-only PDF.
23540
+ *
23541
+ * Renders each page to a canvas, passes them to Tesseract.js (lazy-loaded),
23542
+ * and returns a standard PdfParseResult if text is found.
23543
+ *
23544
+ * Returns null if:
23545
+ * - Tesseract.js cannot be loaded
23546
+ * - OCR produces no usable text across all pages
23547
+ */
23548
+ async runOcrOnPages(pdf, numPages) {
23549
+ const { isOcrAvailable: isOcrAvailable2, runOcr: runOcr2 } = await Promise.resolve().then(() => (init_pdf_ocr_adapter(), pdf_ocr_adapter_exports));
23550
+ if (!await isOcrAvailable2()) {
23551
+ return null;
23552
+ }
23553
+ const SCALE = 2;
23554
+ const pageCanvases = [];
23555
+ const viewportWidths = /* @__PURE__ */ new Map();
23556
+ for (let pageNum = 1; pageNum <= numPages; pageNum++) {
23557
+ const page = await pdf.getPage(pageNum);
23558
+ const viewport = page.getViewport({ scale: 1 });
23559
+ viewportWidths.set(pageNum, viewport.width);
23560
+ const renderViewport = page.getViewport({ scale: SCALE });
23561
+ const canvas = document.createElement("canvas");
23562
+ canvas.width = renderViewport.width;
23563
+ canvas.height = renderViewport.height;
23564
+ const ctx = canvas.getContext("2d");
23565
+ if (!ctx) continue;
23566
+ await page.render({ canvasContext: ctx, viewport: renderViewport }).promise;
23567
+ pageCanvases.push({ pageNumber: pageNum, canvas });
23568
+ }
23569
+ if (pageCanvases.length === 0) return null;
23570
+ const ocrResult = await runOcr2(pageCanvases);
23571
+ const pagesWithText = ocrResult.pages.filter((p) => p.text.length > 0);
23572
+ if (pagesWithText.length === 0) return null;
23573
+ const ocrPages = ocrResult.pages.map((p) => ({
23574
+ pageNumber: p.pageNumber,
23575
+ textContent: p.text,
23576
+ hasImages: true,
23577
+ viewportWidth: viewportWidths.get(p.pageNumber),
23578
+ tablesDetected: 0
23579
+ }));
23580
+ const html = this.pagesToHtml(ocrPages);
23581
+ const allWarnings = [
23582
+ "OCR was used to extract text from this image-only PDF. Quality may vary.",
23583
+ ...ocrResult.warnings
23584
+ ];
23585
+ return {
23586
+ html,
23587
+ pages: ocrPages,
23588
+ metadata: {
23589
+ isTextBased: false,
23590
+ isOcrBased: true,
23591
+ pageCount: numPages
23592
+ },
23593
+ isTextBased: false,
23594
+ warnings: allWarnings
23595
+ };
23596
+ }
23282
23597
  };
23283
23598
  var pdfAdapter = new PdfAdapter();
23284
23599
 
@@ -24682,12 +24997,12 @@ var ToonifyService = class {
24682
24997
  };
24683
24998
  }
24684
24999
  const blocks = this.parseMarkdown(markdown, opts);
24685
- const document2 = { t: "md", blocks };
25000
+ const document3 = { t: "md", blocks };
24686
25001
  const encodeOptions = {
24687
25002
  keyFolding: opts.keyFolding,
24688
25003
  tableFormat: opts.tableFormat
24689
25004
  };
24690
- const compressed = ToonEncoder.encode(document2, encodeOptions);
25005
+ const compressed = ToonEncoder.encode(document3, encodeOptions);
24691
25006
  const compressedTokenEstimate = estimateTokens(compressed);
24692
25007
  const reductionPercent = originalTokenEstimate > 0 ? Math.max(0, Math.min(100, Math.round(
24693
25008
  (originalTokenEstimate - compressedTokenEstimate) / originalTokenEstimate * 100
@@ -25505,8 +25820,8 @@ import { readFileSync as readFileSync4 } from "fs";
25505
25820
  import { resolve, dirname as dirname3 } from "path";
25506
25821
  import { fileURLToPath } from "url";
25507
25822
  var getVersion = () => {
25508
- if ("1.0.24") {
25509
- return "1.0.24";
25823
+ if ("1.0.26") {
25824
+ return "1.0.26";
25510
25825
  }
25511
25826
  try {
25512
25827
  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.24",
3
+ "version": "1.0.26",
4
4
  "description": "FormatFerry CLI — Convert HTML, DOCX, PDF, XLSX, CSV, JSON, XML, PPTX to Markdown",
5
5
  "keywords": [
6
6
  "markdown",