mindee 4.5.0 → 4.6.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v4.6.1 - 2023-12-15
4
+ ### Changes
5
+ * :recycle: tweak async delays & retry
6
+ * :recycle: tweak default async sample delays & retry
7
+ * :memo: update md doc & fix typos
8
+
9
+
10
+ ## v4.6.0 - 2023-12-11
11
+ ### Changes
12
+ * :sparkles: add invoice-splitter auto-splitting feature
13
+ * :memo: add examples to illustrate auto-splitting feature
14
+ * :recycle: add unit tests for auto-splitting feature
15
+ * :recycle: rename `exemple` folder to `example`
16
+ * :arrow_up: update testing library
17
+
18
+
3
19
  ## v4.5.0 - 2023-11-29
4
20
  ### Changes
5
21
  * :sparkles: add Multi-Receipts auto-extraction feature
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindee",
3
- "version": "4.5.0",
3
+ "version": "4.6.1",
4
4
  "description": "Mindee Client Library for Node.js",
5
5
  "main": "src/index.js",
6
6
  "bin": "bin/mindee.js",
package/src/cli.js CHANGED
@@ -228,9 +228,9 @@ async function callEnqueueAndParse(productClass, command, inputPath, options) {
228
228
  pageOptions: pageOptions,
229
229
  allWords: predictParams.allWords,
230
230
  cropper: predictParams.cropper,
231
- initialDelaySec: 6,
232
- delaySec: 3,
233
- maxRetries: 10,
231
+ initialDelaySec: 4,
232
+ delaySec: 2,
233
+ maxRetries: 30,
234
234
  });
235
235
  }
236
236
  else {
@@ -238,9 +238,9 @@ async function callEnqueueAndParse(productClass, command, inputPath, options) {
238
238
  pageOptions: pageOptions,
239
239
  allWords: predictParams.allWords,
240
240
  cropper: predictParams.cropper,
241
- initialDelaySec: 6,
242
- delaySec: 3,
243
- maxRetries: 10,
241
+ initialDelaySec: 4,
242
+ delaySec: 2,
243
+ maxRetries: 30,
244
244
  });
245
245
  if (!response.document) {
246
246
  throw Error("Document could not be retrieved");
package/src/client.js CHANGED
@@ -153,9 +153,9 @@ class Client {
153
153
  allWords: undefined,
154
154
  cropper: undefined,
155
155
  pageOptions: undefined,
156
- initialDelaySec: 6,
157
- delaySec: 3,
158
- maxRetries: 10,
156
+ initialDelaySec: 4,
157
+ delaySec: 2,
158
+ maxRetries: 30,
159
159
  initialTimerOptions: undefined,
160
160
  recurringTimerOptions: undefined,
161
161
  }) {
@@ -283,17 +283,20 @@ Job status: ${pollResults.job.status}.`);
283
283
  }
284
284
  exports.Client = Client;
285
285
  _Client_instances = new WeakSet(), _Client_validateAsyncParams = function _Client_validateAsyncParams(asyncParams) {
286
- asyncParams.delaySec ?? (asyncParams.delaySec = 3);
287
- asyncParams.initialDelaySec ?? (asyncParams.initialDelaySec = 6);
288
- asyncParams.maxRetries ?? (asyncParams.maxRetries = 10);
289
- if (asyncParams.delaySec < 2) {
290
- throw Error("Cannot set auto-parsing delay to less than 2 seconds.");
286
+ const minDelaySec = 1;
287
+ const minInitialDelay = 2;
288
+ const minRetries = 2;
289
+ asyncParams.delaySec ?? (asyncParams.delaySec = 2);
290
+ asyncParams.initialDelaySec ?? (asyncParams.initialDelaySec = 4);
291
+ asyncParams.maxRetries ?? (asyncParams.maxRetries = 30);
292
+ if (asyncParams.delaySec < minDelaySec) {
293
+ throw Error(`Cannot set auto-parsing delay to less than ${minDelaySec} seconds.`);
291
294
  }
292
- if (asyncParams.initialDelaySec < 4) {
293
- throw Error("Cannot set initial parsing delay to less than 4 seconds.");
295
+ if (asyncParams.initialDelaySec < minInitialDelay) {
296
+ throw Error(`Cannot set initial parsing delay to less than ${minInitialDelay} seconds.`);
294
297
  }
295
- if (!Number.isInteger(asyncParams.maxRetries)) {
296
- throw Error("Retry amount must be an integer.");
298
+ if (asyncParams.maxRetries < minRetries) {
299
+ throw Error(`Cannot set retry to less than ${minRetries}.`);
297
300
  }
298
301
  }, _Client_buildProductEndpoint = function _Client_buildProductEndpoint(endpointName, accountName, endpointVersion) {
299
302
  return new http_1.Endpoint(endpointName, accountName, endpointVersion, __classPrivateFieldGet(this, _Client_instances, "m", _Client_buildApiSettings).call(this));
@@ -1,8 +1,20 @@
1
1
  /// <reference types="node" />
2
2
  import { Buffer } from "node:buffer";
3
- import { BufferInput } from "../input/sources";
4
- export interface ExtractedImage {
5
- readonly pageId: number;
6
- imageData: Buffer;
3
+ import { BufferInput } from "../input";
4
+ export declare abstract class ExtractedImage {
5
+ buffer: Buffer;
6
+ protected internalFileName: string;
7
+ constructor(buffer: Uint8Array, fileName: string);
8
+ /**
9
+ * Saves the document to a file.
10
+ *
11
+ * @param outputPath Path to save the file to.
12
+ */
13
+ saveToFile(outputPath: string): void;
14
+ /**
15
+ * Return the file as a Mindee-compatible BufferInput source.
16
+ *
17
+ * @returns A BufferInput source.
18
+ */
7
19
  asSource(): BufferInput;
8
20
  }
@@ -1,2 +1,49 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ExtractedImage = void 0;
7
+ const node_buffer_1 = require("node:buffer");
8
+ const errors_1 = require("../errors");
9
+ const node_fs_1 = require("node:fs");
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const logger_1 = require("../logger");
12
+ const input_1 = require("../input");
13
+ class ExtractedImage {
14
+ constructor(buffer, fileName) {
15
+ this.buffer = node_buffer_1.Buffer.from(buffer);
16
+ this.internalFileName = fileName;
17
+ }
18
+ /**
19
+ * Saves the document to a file.
20
+ *
21
+ * @param outputPath Path to save the file to.
22
+ */
23
+ saveToFile(outputPath) {
24
+ try {
25
+ (0, node_fs_1.writeFileSync)(node_path_1.default.resolve(outputPath), this.buffer);
26
+ logger_1.logger.info(`File saved successfully to ${node_path_1.default.resolve(outputPath)}.`);
27
+ }
28
+ catch (e) {
29
+ if (e instanceof TypeError) {
30
+ throw new errors_1.MindeeError("Invalid path/filename provided.");
31
+ }
32
+ else {
33
+ throw e;
34
+ }
35
+ }
36
+ }
37
+ /**
38
+ * Return the file as a Mindee-compatible BufferInput source.
39
+ *
40
+ * @returns A BufferInput source.
41
+ */
42
+ asSource() {
43
+ return new input_1.BufferInput({
44
+ buffer: this.buffer,
45
+ filename: this.internalFileName,
46
+ });
47
+ }
48
+ }
49
+ exports.ExtractedImage = ExtractedImage;
@@ -0,0 +1,6 @@
1
+ import { ExtractedImage } from "./extractedImage";
2
+ export declare class ExtractedInvoiceSplitterImage extends ExtractedImage {
3
+ readonly pageIdMin: number;
4
+ readonly pageIdMax: number;
5
+ constructor(buffer: Uint8Array, pageIndices: [number, number]);
6
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExtractedInvoiceSplitterImage = void 0;
4
+ const extractedImage_1 = require("./extractedImage");
5
+ class ExtractedInvoiceSplitterImage extends extractedImage_1.ExtractedImage {
6
+ constructor(buffer, pageIndices) {
7
+ super(buffer, `invoice_p_${pageIndices[0]}-${pageIndices[1]}.pdf`);
8
+ this.pageIdMin = pageIndices[0];
9
+ this.pageIdMax = pageIndices[1];
10
+ }
11
+ }
12
+ exports.ExtractedInvoiceSplitterImage = ExtractedInvoiceSplitterImage;
@@ -1,10 +1,6 @@
1
- /// <reference types="node" />
2
- import { BufferInput } from "../input";
3
1
  import { ExtractedImage } from "./extractedImage";
4
- export declare class ExtractedMultiReceiptImage implements ExtractedImage {
2
+ export declare class ExtractedMultiReceiptImage extends ExtractedImage {
5
3
  readonly receiptId: number;
6
4
  readonly pageId: number;
7
- imageData: Buffer;
8
- constructor(imageData: Uint8Array, pageId: number, receiptId: number);
9
- asSource(): BufferInput;
5
+ constructor(buffer: Uint8Array, pageId: number, receiptId: number);
10
6
  }
@@ -1,18 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExtractedMultiReceiptImage = void 0;
4
- const input_1 = require("../input");
5
- class ExtractedMultiReceiptImage {
6
- constructor(imageData, pageId, receiptId) {
4
+ const extractedImage_1 = require("./extractedImage");
5
+ class ExtractedMultiReceiptImage extends extractedImage_1.ExtractedImage {
6
+ constructor(buffer, pageId, receiptId) {
7
+ super(buffer, `receipt_p${pageId}_${receiptId}.pdf`);
7
8
  this.pageId = pageId;
8
- this.imageData = Buffer.from(imageData);
9
9
  this.receiptId = receiptId;
10
10
  }
11
- asSource() {
12
- return new input_1.BufferInput({
13
- buffer: this.imageData,
14
- filename: `receipt_p${this.pageId}_${this.receiptId}.pdf`,
15
- });
16
- }
17
11
  }
18
12
  exports.ExtractedMultiReceiptImage = ExtractedMultiReceiptImage;
@@ -1,2 +1,4 @@
1
1
  export { extractReceipts } from "./multiReceiptsExtractor";
2
+ export { extractInvoices } from "./invoiceSplitterExtractor";
2
3
  export { ExtractedMultiReceiptImage } from "./extractedMultiReceiptImage";
4
+ export { ExtractedInvoiceSplitterImage } from "./extractedInvoiceSplitterDocument";
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ExtractedMultiReceiptImage = exports.extractReceipts = void 0;
3
+ exports.ExtractedInvoiceSplitterImage = exports.ExtractedMultiReceiptImage = exports.extractInvoices = exports.extractReceipts = void 0;
4
4
  var multiReceiptsExtractor_1 = require("./multiReceiptsExtractor");
5
5
  Object.defineProperty(exports, "extractReceipts", { enumerable: true, get: function () { return multiReceiptsExtractor_1.extractReceipts; } });
6
+ var invoiceSplitterExtractor_1 = require("./invoiceSplitterExtractor");
7
+ Object.defineProperty(exports, "extractInvoices", { enumerable: true, get: function () { return invoiceSplitterExtractor_1.extractInvoices; } });
6
8
  var extractedMultiReceiptImage_1 = require("./extractedMultiReceiptImage");
7
9
  Object.defineProperty(exports, "ExtractedMultiReceiptImage", { enumerable: true, get: function () { return extractedMultiReceiptImage_1.ExtractedMultiReceiptImage; } });
10
+ var extractedInvoiceSplitterDocument_1 = require("./extractedInvoiceSplitterDocument");
11
+ Object.defineProperty(exports, "ExtractedInvoiceSplitterImage", { enumerable: true, get: function () { return extractedInvoiceSplitterDocument_1.ExtractedInvoiceSplitterImage; } });
@@ -1,3 +1,5 @@
1
1
  export { extractReceipts } from "./multiReceiptsExtractor";
2
+ export { extractInvoices } from "./invoiceSplitterExtractor";
2
3
  export { ExtractedMultiReceiptImage } from "./extractedMultiReceiptImage";
4
+ export { ExtractedInvoiceSplitterImage } from "./extractedInvoiceSplitterDocument";
3
5
  export { ExtractedImage } from "./extractedImage";
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ExtractedMultiReceiptImage = exports.extractReceipts = void 0;
3
+ exports.ExtractedImage = exports.ExtractedInvoiceSplitterImage = exports.ExtractedMultiReceiptImage = exports.extractInvoices = exports.extractReceipts = void 0;
4
4
  var multiReceiptsExtractor_1 = require("./multiReceiptsExtractor");
5
5
  Object.defineProperty(exports, "extractReceipts", { enumerable: true, get: function () { return multiReceiptsExtractor_1.extractReceipts; } });
6
+ var invoiceSplitterExtractor_1 = require("./invoiceSplitterExtractor");
7
+ Object.defineProperty(exports, "extractInvoices", { enumerable: true, get: function () { return invoiceSplitterExtractor_1.extractInvoices; } });
6
8
  var extractedMultiReceiptImage_1 = require("./extractedMultiReceiptImage");
7
9
  Object.defineProperty(exports, "ExtractedMultiReceiptImage", { enumerable: true, get: function () { return extractedMultiReceiptImage_1.ExtractedMultiReceiptImage; } });
10
+ var extractedInvoiceSplitterDocument_1 = require("./extractedInvoiceSplitterDocument");
11
+ Object.defineProperty(exports, "ExtractedInvoiceSplitterImage", { enumerable: true, get: function () { return extractedInvoiceSplitterDocument_1.ExtractedInvoiceSplitterImage; } });
12
+ var extractedImage_1 = require("./extractedImage");
13
+ Object.defineProperty(exports, "ExtractedImage", { enumerable: true, get: function () { return extractedImage_1.ExtractedImage; } });
@@ -0,0 +1,12 @@
1
+ import { InvoiceSplitterV1 } from "../product";
2
+ import { LocalInputSource } from "../input/base";
3
+ import { ExtractedInvoiceSplitterImage } from "./extractedInvoiceSplitterDocument";
4
+ /**
5
+ * Extracts & cuts the pages of a main document invoice according to the provided indexes.
6
+ *
7
+ * @param inputFile File to extract sub-invoices from.
8
+ * @param indexes List of indexes to cut the document according to. Can be provided either as a InvoiceSplitterV1 inference, or a direct list of splits.
9
+ * @param strict If set to true, doesn't cut pages where the API isn't 100% confident.
10
+ * @returns A promise of extracted images, as an array of ExtractedInvoiceSplitterImage.
11
+ */
12
+ export declare function extractInvoices(inputFile: LocalInputSource, indexes: InvoiceSplitterV1 | number[][], strict?: boolean): Promise<ExtractedInvoiceSplitterImage[]>;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractInvoices = void 0;
4
+ const pdf_lib_1 = require("pdf-lib");
5
+ const errors_1 = require("../errors");
6
+ const product_1 = require("../product");
7
+ const extractedInvoiceSplitterDocument_1 = require("./extractedInvoiceSplitterDocument");
8
+ async function splitPdf(pdfDoc, invoicePageGroups) {
9
+ if (invoicePageGroups.length === 0) {
10
+ return [];
11
+ }
12
+ const generatedPdfs = [];
13
+ for (let i = 0; i < invoicePageGroups.length; i++) {
14
+ const subdocument = await pdf_lib_1.PDFDocument.create();
15
+ const fullIndexes = [];
16
+ for (let j = invoicePageGroups[i][0]; j <= invoicePageGroups[i][invoicePageGroups[i].length - 1]; j++) {
17
+ fullIndexes.push(j);
18
+ }
19
+ const copiedPages = await subdocument.copyPages(pdfDoc, fullIndexes);
20
+ copiedPages.map((page) => {
21
+ subdocument.addPage(page);
22
+ });
23
+ const subdocumentBytes = await subdocument.save();
24
+ generatedPdfs.push(new extractedInvoiceSplitterDocument_1.ExtractedInvoiceSplitterImage(subdocumentBytes, [invoicePageGroups[i][0], invoicePageGroups[i][invoicePageGroups[i].length - 1]]));
25
+ }
26
+ return generatedPdfs;
27
+ }
28
+ async function getPdfDoc(inputFile) {
29
+ await inputFile.init();
30
+ if (!inputFile.isPdf()) {
31
+ throw new errors_1.MindeeMimeTypeError("Invoice Splitter is only compatible with pdf documents.");
32
+ }
33
+ const pdfDoc = await pdf_lib_1.PDFDocument.load(inputFile.fileObject);
34
+ if (pdfDoc.getPageCount() < 2) {
35
+ throw new errors_1.MindeeError("Invoice Splitter is only compatible with multi-page-pdf documents.");
36
+ }
37
+ return pdfDoc;
38
+ }
39
+ /**
40
+ * Extracts & cuts the pages of a main document invoice according to the provided indexes.
41
+ *
42
+ * @param inputFile File to extract sub-invoices from.
43
+ * @param indexes List of indexes to cut the document according to. Can be provided either as a InvoiceSplitterV1 inference, or a direct list of splits.
44
+ * @param strict If set to true, doesn't cut pages where the API isn't 100% confident.
45
+ * @returns A promise of extracted images, as an array of ExtractedInvoiceSplitterImage.
46
+ */
47
+ async function extractInvoices(inputFile, indexes, strict = false) {
48
+ if (!indexes) {
49
+ throw new errors_1.MindeeError("No possible receipts candidates found for MultiReceipts extraction.");
50
+ }
51
+ let customIndexes = [];
52
+ if (indexes instanceof product_1.InvoiceSplitterV1) {
53
+ indexes.prediction.invoicePageGroups.map((invoicePageGroup) => {
54
+ if (!strict || invoicePageGroup.confidence === 1) {
55
+ customIndexes.push(invoicePageGroup.pageIndexes);
56
+ }
57
+ });
58
+ }
59
+ else {
60
+ customIndexes = indexes;
61
+ }
62
+ const pdfDoc = await getPdfDoc(inputFile);
63
+ const pageCount = pdfDoc.getPageCount();
64
+ customIndexes.forEach((pageGroup) => {
65
+ pageGroup.forEach((index) => {
66
+ if (index >= pageCount) {
67
+ throw new errors_1.MindeeError(`Given index ${index} doesn't exist in page range (0-${pageCount - 1})`);
68
+ }
69
+ });
70
+ });
71
+ return await splitPdf(pdfDoc, customIndexes);
72
+ }
73
+ exports.extractInvoices = extractInvoices;
@@ -1,4 +1,11 @@
1
1
  import { MultiReceiptsDetectorV1 } from "../product";
2
2
  import { ExtractedMultiReceiptImage } from "./extractedMultiReceiptImage";
3
3
  import { LocalInputSource } from "../input/base";
4
+ /**
5
+ * Extracts individual receipts from multi-receipts documents.
6
+ *
7
+ * @param inputFile File to extract sub-receipts from.
8
+ * @param inference Results of the inference.
9
+ * @returns Individual extracted receipts as an array of ExtractedMultiReceiptsImage.
10
+ */
4
11
  export declare function extractReceipts(inputFile: LocalInputSource, inference: MultiReceiptsDetectorV1): Promise<ExtractedMultiReceiptImage[]>;
@@ -10,7 +10,6 @@ async function addPage(pdfPage, boundingBox, pageId, receiptId) {
10
10
  const receiptPdf = await pdf_lib_1.PDFDocument.create();
11
11
  const newWidth = width * ((0, geometry_1.getMinMaxX)(boundingBox).max - (0, geometry_1.getMinMaxX)(boundingBox).min);
12
12
  const newHeight = height * ((0, geometry_1.getMinMaxY)(boundingBox).max - (0, geometry_1.getMinMaxY)(boundingBox).min);
13
- //Note: PDF-lib seems to invert y coordinates, giving us the following horror:
14
13
  const croppedReceipt = await receiptPdf.embedPage(pdfPage, {
15
14
  left: (0, geometry_1.getMinMaxX)(boundingBox).min * width,
16
15
  right: (0, geometry_1.getMinMaxX)(boundingBox).max * width,
@@ -48,6 +47,13 @@ async function loadPdfDoc(inputFile) {
48
47
  }
49
48
  return pdfDoc;
50
49
  }
50
+ /**
51
+ * Extracts individual receipts from multi-receipts documents.
52
+ *
53
+ * @param inputFile File to extract sub-receipts from.
54
+ * @param inference Results of the inference.
55
+ * @returns Individual extracted receipts as an array of ExtractedMultiReceiptsImage.
56
+ */
51
57
  async function extractReceipts(inputFile, inference) {
52
58
  const images = [];
53
59
  if (!inference.prediction.receipts) {
@@ -29,7 +29,7 @@ export declare abstract class LocalInputSource extends InputSource {
29
29
  isPdf(): boolean;
30
30
  checkMimetype(): Promise<string>;
31
31
  /**
32
- * Merge PDF pages.
32
+ * Cut PDF pages.
33
33
  * @param pageOptions
34
34
  */
35
35
  cutPdf(pageOptions: PageOptions): Promise<void>;
package/src/input/base.js CHANGED
@@ -106,7 +106,7 @@ class LocalInputSource extends InputSource {
106
106
  return mimeType;
107
107
  }
108
108
  /**
109
- * Merge PDF pages.
109
+ * Cut PDF pages.
110
110
  * @param pageOptions
111
111
  */
112
112
  async cutPdf(pageOptions) {