mindee 5.1.1 → 5.2.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.
- package/CHANGELOG.md +11 -0
- package/package.json +5 -5
- package/src/image/extractedImage.d.ts +4 -2
- package/src/image/extractedImage.js +5 -3
- package/src/image/imageExtractor.d.ts +13 -2
- package/src/image/imageExtractor.js +50 -12
- package/src/pdf/extractedPdf.d.ts +25 -0
- package/src/pdf/extractedPdf.js +65 -0
- package/src/pdf/pdfCompressor.js +2 -40
- package/src/pdf/pdfExtractor.d.ts +46 -0
- package/src/pdf/pdfExtractor.js +128 -0
- package/src/pdf/pdfOperation.d.ts +7 -0
- package/src/pdf/pdfOperation.js +34 -1
- package/src/pdf/pdfUtils.d.ts +9 -0
- package/src/pdf/pdfUtils.js +40 -0
- package/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.js +6 -33
- package/src/v2/fileOperations/crop.d.ts +18 -0
- package/src/v2/fileOperations/crop.js +35 -0
- package/src/v2/fileOperations/cropFiles.d.ts +4 -0
- package/src/v2/fileOperations/cropFiles.js +5 -0
- package/src/v2/fileOperations/index.d.ts +1 -0
- package/src/v2/fileOperations/index.js +1 -0
- package/src/v2/fileOperations/split.d.ts +21 -0
- package/src/v2/fileOperations/split.js +48 -0
- package/src/v2/fileOperations/splitFiles.d.ts +4 -0
- package/src/v2/fileOperations/splitFiles.js +5 -0
- package/src/v2/product/crop/cropItem.d.ts +8 -0
- package/src/v2/product/crop/cropItem.js +9 -0
- package/src/v2/product/crop/cropResponse.d.ts +9 -1
- package/src/v2/product/crop/cropResponse.js +10 -1
- package/src/v2/product/split/splitRange.d.ts +6 -0
- package/src/v2/product/split/splitRange.js +9 -0
- package/src/v2/product/split/splitResponse.d.ts +7 -0
- package/src/v2/product/split/splitResponse.js +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Mindee Node.js API Library Changelog
|
|
2
2
|
|
|
3
|
+
## v5.2.0 - 2026-04-14
|
|
4
|
+
### Changes
|
|
5
|
+
* :sparkles: add support for crop operation for V2
|
|
6
|
+
* :sparkles: add support for split operation for V2
|
|
7
|
+
* :arrow_up: bump dependencies
|
|
8
|
+
### Fixes
|
|
9
|
+
* :bug: fix quality defaulting to 4.17 (300/72) instead of 1 when extracting cropped zones
|
|
10
|
+
* :bug: fix split extraction not allowing for image files
|
|
11
|
+
* :bug: fix naming in extracted PDFs
|
|
12
|
+
|
|
13
|
+
|
|
3
14
|
## v5.1.1 - 2026-03-11
|
|
4
15
|
### Changes
|
|
5
16
|
* :heavy_minus_sign: tslib is never imported, remove from dependencies
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mindee",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "Mindee Client Library for Node.js",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mindee",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"commander": "^14.0.3",
|
|
51
|
-
"file-type": "^21.3.
|
|
51
|
+
"file-type": "^21.3.3",
|
|
52
52
|
"tmp": "^0.2.5",
|
|
53
|
-
"undici": ">=6.
|
|
53
|
+
"undici": ">=6.24.0 <7.0.0 || >=7.24.0 <8.0.0"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
56
|
"@cantoo/pdf-lib": "^2.5.3",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@types/node": "^20.19.37",
|
|
66
66
|
"@types/tmp": "^0.2.6",
|
|
67
67
|
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
68
|
-
"@typescript-eslint/parser": "^8.
|
|
68
|
+
"@typescript-eslint/parser": "^8.58.1",
|
|
69
69
|
"eslint": "^10.0.3",
|
|
70
70
|
"eslint-plugin-jsdoc": "^62.7.1",
|
|
71
71
|
"globals": "^17.4.0",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"tsc-alias": "^1.8.16",
|
|
74
74
|
"tslib": "^2.8.1",
|
|
75
75
|
"tsx": "^4.21.0",
|
|
76
|
-
"typedoc": "^0.28.
|
|
76
|
+
"typedoc": "^0.28.18",
|
|
77
77
|
"typescript": "^5.9.3"
|
|
78
78
|
},
|
|
79
79
|
"keywords": [
|
|
@@ -5,8 +5,10 @@ import { BufferInput } from "../input/index.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export declare class ExtractedImage {
|
|
7
7
|
buffer: Buffer;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
filename: string;
|
|
9
|
+
pageId?: number;
|
|
10
|
+
elementId?: number;
|
|
11
|
+
constructor(buffer: Uint8Array, fileName: string, pageId?: number, elementId?: number);
|
|
10
12
|
/**
|
|
11
13
|
* Saves the document to a file.
|
|
12
14
|
*
|
|
@@ -10,9 +10,11 @@ import { loadOptionalDependency } from "../dependency/index.js";
|
|
|
10
10
|
* Generic class for image extraction
|
|
11
11
|
*/
|
|
12
12
|
export class ExtractedImage {
|
|
13
|
-
constructor(buffer, fileName) {
|
|
13
|
+
constructor(buffer, fileName, pageId, elementId) {
|
|
14
14
|
this.buffer = Buffer.from(buffer);
|
|
15
|
-
this.
|
|
15
|
+
this.filename = fileName;
|
|
16
|
+
this.pageId = pageId;
|
|
17
|
+
this.elementId = elementId;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Saves the document to a file.
|
|
@@ -93,7 +95,7 @@ export class ExtractedImage {
|
|
|
93
95
|
asSource() {
|
|
94
96
|
return new BufferInput({
|
|
95
97
|
buffer: this.buffer,
|
|
96
|
-
filename: this.
|
|
98
|
+
filename: this.filename,
|
|
97
99
|
});
|
|
98
100
|
}
|
|
99
101
|
}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import type * as pdfLibTypes from "@cantoo/pdf-lib";
|
|
2
1
|
import { Polygon } from "../geometry/index.js";
|
|
2
|
+
import { ExtractedImage } from "../image/extractedImage.js";
|
|
3
|
+
import { LocalInputSource } from "../input/index.js";
|
|
4
|
+
import type * as pdfLibTypes from "@cantoo/pdf-lib";
|
|
5
|
+
/**
|
|
6
|
+
* Extracts elements from a PDF document based on a list of bounding boxes.
|
|
7
|
+
* @param inputSource The input source to extract from.
|
|
8
|
+
* @param polygonsPerPage List of polygons to extract from per page.
|
|
9
|
+
* @param quality JPEG quality of extracted images.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractImagesFromPolygon(inputSource: LocalInputSource, polygonsPerPage: Map<number, Polygon[]>, quality?: number): Promise<ExtractedImage[]>;
|
|
3
12
|
/**
|
|
4
13
|
* Extracts elements from a page based off of a list of bounding boxes.
|
|
5
14
|
*
|
|
6
15
|
* @param pdfPage PDF Page to extract from.
|
|
7
16
|
* @param polygons List of coordinates to pull the elements from.
|
|
17
|
+
* @param asImage Whether to return the extracted elements as images.
|
|
18
|
+
* @param quality JPEG quality of extracted images, given as number between 0 and 1.
|
|
8
19
|
*/
|
|
9
|
-
export declare function extractFromPage(pdfPage: pdfLibTypes.PDFPage, polygons: Polygon[]): Promise<Uint8Array<ArrayBufferLike>[]>;
|
|
20
|
+
export declare function extractFromPage(pdfPage: pdfLibTypes.PDFPage, polygons: Polygon[], asImage?: boolean, quality?: number): Promise<Uint8Array<ArrayBufferLike>[]>;
|
|
@@ -1,44 +1,76 @@
|
|
|
1
|
+
import { loadOptionalDependency } from "../dependency/index.js";
|
|
2
|
+
import { MindeeImageError } from "../errors/index.js";
|
|
1
3
|
import { getMinMaxX, getMinMaxY } from "../geometry/index.js";
|
|
2
4
|
import { adjustForRotation } from "../geometry/polygonUtils.js";
|
|
3
|
-
import {
|
|
5
|
+
import { ExtractedImage } from "../image/extractedImage.js";
|
|
6
|
+
import { logger } from "../logger.js";
|
|
7
|
+
import { createPdfFromInputSource } from "../pdf/pdfOperation.js";
|
|
8
|
+
import { rasterizePage } from "../pdf/pdfUtils.js";
|
|
4
9
|
let pdfLib = null;
|
|
5
10
|
async function getPdfLib() {
|
|
6
11
|
if (!pdfLib) {
|
|
7
|
-
const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "
|
|
12
|
+
const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Image Extraction");
|
|
8
13
|
pdfLib = pdfLibImport.default || pdfLibImport;
|
|
9
14
|
}
|
|
10
15
|
return pdfLib;
|
|
11
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Extracts elements from a PDF document based on a list of bounding boxes.
|
|
19
|
+
* @param inputSource The input source to extract from.
|
|
20
|
+
* @param polygonsPerPage List of polygons to extract from per page.
|
|
21
|
+
* @param quality JPEG quality of extracted images.
|
|
22
|
+
*/
|
|
23
|
+
export async function extractImagesFromPolygon(inputSource, polygonsPerPage, quality) {
|
|
24
|
+
const allExtractedImages = [];
|
|
25
|
+
const pdfDoc = await createPdfFromInputSource(inputSource);
|
|
26
|
+
for (const [pageId, polygons] of polygonsPerPage) {
|
|
27
|
+
logger.debug(`Extracting images from page ${pageId}`);
|
|
28
|
+
const pdfPage = pdfDoc.getPage(pageId);
|
|
29
|
+
const extractions = (await extractFromPage(pdfPage, polygons, true, quality));
|
|
30
|
+
const extractedImages = extractions.map((v, i) => new ExtractedImage(v, inputSource.filename + `_page${pageId}-${i}.jpg`, pageId, i));
|
|
31
|
+
allExtractedImages.push(...extractedImages);
|
|
32
|
+
}
|
|
33
|
+
return allExtractedImages;
|
|
34
|
+
}
|
|
12
35
|
/**
|
|
13
36
|
* Extracts elements from a page based off of a list of bounding boxes.
|
|
14
37
|
*
|
|
15
38
|
* @param pdfPage PDF Page to extract from.
|
|
16
39
|
* @param polygons List of coordinates to pull the elements from.
|
|
40
|
+
* @param asImage Whether to return the extracted elements as images.
|
|
41
|
+
* @param quality JPEG quality of extracted images, given as number between 0 and 1.
|
|
17
42
|
*/
|
|
18
|
-
export async function extractFromPage(pdfPage, polygons) {
|
|
43
|
+
export async function extractFromPage(pdfPage, polygons, asImage = false, quality) {
|
|
19
44
|
const pdfLib = await getPdfLib();
|
|
20
45
|
const { width, height } = pdfPage.getSize();
|
|
21
46
|
const extractedElements = [];
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
47
|
+
if (quality && (quality < 0)) {
|
|
48
|
+
throw new MindeeImageError("Quality must be a number between 0 and 1");
|
|
49
|
+
}
|
|
50
|
+
if (quality && quality > 1) {
|
|
51
|
+
logger.warn("Quality is greater than 1, this operation will apply a manual upscale on the output." +
|
|
52
|
+
" Use only if you know what you are doing.");
|
|
53
|
+
}
|
|
54
|
+
const qualityScale = quality ?? 1;
|
|
25
55
|
const orientation = pdfPage.getRotation().angle;
|
|
56
|
+
const sourceDoc = pdfPage.doc;
|
|
57
|
+
const pageIndex = sourceDoc.getPages().indexOf(pdfPage);
|
|
26
58
|
for (const origPolygon of polygons) {
|
|
27
|
-
|
|
59
|
+
logger.debug(`Extracting image with polygon: ${origPolygon.toString()}`);
|
|
28
60
|
const tempPdf = await pdfLib.PDFDocument.create();
|
|
61
|
+
const [copiedPage] = await tempPdf.copyPages(sourceDoc, [pageIndex]);
|
|
62
|
+
const polygon = adjustForRotation(origPolygon, orientation);
|
|
29
63
|
const newWidth = width * (getMinMaxX(polygon).max - getMinMaxX(polygon).min);
|
|
30
64
|
const newHeight = height * (getMinMaxY(polygon).max - getMinMaxY(polygon).min);
|
|
31
|
-
const cropped = await tempPdf.embedPage(
|
|
65
|
+
const cropped = await tempPdf.embedPage(copiedPage, {
|
|
32
66
|
left: getMinMaxX(polygon).min * width,
|
|
33
67
|
right: getMinMaxX(polygon).max * width,
|
|
34
68
|
top: height - (getMinMaxY(polygon).min * height),
|
|
35
69
|
bottom: height - (getMinMaxY(polygon).max * height),
|
|
36
70
|
});
|
|
37
|
-
// Determine the final page dimensions based on orientation
|
|
38
71
|
let finalWidth;
|
|
39
72
|
let finalHeight;
|
|
40
73
|
if (orientation === 90 || orientation === 270) {
|
|
41
|
-
// For 90/270 rotations, swap width and height
|
|
42
74
|
finalWidth = newHeight * qualityScale;
|
|
43
75
|
finalHeight = newWidth * qualityScale;
|
|
44
76
|
}
|
|
@@ -52,8 +84,8 @@ export async function extractFromPage(pdfPage, polygons) {
|
|
|
52
84
|
y: 0,
|
|
53
85
|
width: finalWidth,
|
|
54
86
|
height: finalHeight,
|
|
87
|
+
color: pdfLib.rgb(1, 1, 1),
|
|
55
88
|
});
|
|
56
|
-
// Draw the cropped page with rotation applied
|
|
57
89
|
if (orientation === 0) {
|
|
58
90
|
samplePage.drawPage(cropped, {
|
|
59
91
|
width: newWidth * qualityScale,
|
|
@@ -87,7 +119,13 @@ export async function extractFromPage(pdfPage, polygons) {
|
|
|
87
119
|
rotate: pdfLib.degrees(90),
|
|
88
120
|
});
|
|
89
121
|
}
|
|
90
|
-
|
|
122
|
+
const pdfBuffer = Buffer.from(await tempPdf.save());
|
|
123
|
+
if (asImage) {
|
|
124
|
+
extractedElements.push(await rasterizePage(pdfBuffer, 0, 100));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
extractedElements.push(pdfBuffer);
|
|
128
|
+
}
|
|
91
129
|
}
|
|
92
130
|
return extractedElements;
|
|
93
131
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BufferInput } from "../input/index.js";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
export declare class ExtractedPdf {
|
|
4
|
+
readonly buffer: Buffer;
|
|
5
|
+
readonly filename: string;
|
|
6
|
+
readonly pageCount: number;
|
|
7
|
+
constructor(pdfData: Buffer<ArrayBufferLike>, filename: string, pageCount: number);
|
|
8
|
+
/**
|
|
9
|
+
* Saves the document to a file.
|
|
10
|
+
*
|
|
11
|
+
* @param outputPath Path to save the file to.
|
|
12
|
+
*/
|
|
13
|
+
saveToFileAsync(outputPath: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Saves the document to a file synchronously.
|
|
16
|
+
* @param outputPath
|
|
17
|
+
*/
|
|
18
|
+
saveToFile(outputPath: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Return the file as a Mindee-compatible BufferInput source.
|
|
21
|
+
*
|
|
22
|
+
* @returns A BufferInput source.
|
|
23
|
+
*/
|
|
24
|
+
asSource(): BufferInput;
|
|
25
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { BufferInput, MIMETYPES } from "../input/index.js";
|
|
3
|
+
import { MindeeError } from "../errors/index.js";
|
|
4
|
+
import { writeFile } from "fs/promises";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
6
|
+
import { writeFileSync } from "node:fs";
|
|
7
|
+
export class ExtractedPdf {
|
|
8
|
+
constructor(pdfData, filename, pageCount) {
|
|
9
|
+
this.buffer = pdfData;
|
|
10
|
+
this.filename = filename;
|
|
11
|
+
this.pageCount = pageCount;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Saves the document to a file.
|
|
15
|
+
*
|
|
16
|
+
* @param outputPath Path to save the file to.
|
|
17
|
+
*/
|
|
18
|
+
async saveToFileAsync(outputPath) {
|
|
19
|
+
const fileExt = path.extname(outputPath).toLowerCase();
|
|
20
|
+
if (fileExt !== ".pdf" && !MIMETYPES.has(fileExt)) {
|
|
21
|
+
outputPath += ".pdf";
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
await writeFile(path.resolve(outputPath), this.buffer);
|
|
25
|
+
logger.info(`File saved successfully to ${path.resolve(outputPath)}.`);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
if (e instanceof TypeError) {
|
|
29
|
+
throw new MindeeError("Invalid path/filename provided.");
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Saves the document to a file synchronously.
|
|
38
|
+
* @param outputPath
|
|
39
|
+
*/
|
|
40
|
+
saveToFile(outputPath) {
|
|
41
|
+
try {
|
|
42
|
+
writeFileSync(path.resolve(outputPath), this.buffer);
|
|
43
|
+
logger.info(`File saved successfully to ${path.resolve(outputPath)}.`);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
if (e instanceof TypeError) {
|
|
47
|
+
throw new MindeeError("Invalid path/filename provided.");
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Return the file as a Mindee-compatible BufferInput source.
|
|
56
|
+
*
|
|
57
|
+
* @returns A BufferInput source.
|
|
58
|
+
*/
|
|
59
|
+
asSource() {
|
|
60
|
+
return new BufferInput({
|
|
61
|
+
buffer: this.buffer,
|
|
62
|
+
filename: this.filename,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/pdf/pdfCompressor.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { logger } from "../logger.js";
|
|
2
|
-
import tmp from "tmp";
|
|
3
|
-
import * as fs from "node:fs";
|
|
4
2
|
import { compressImage } from "../image/index.js";
|
|
5
3
|
import { loadOptionalDependency } from "../dependency/index.js";
|
|
6
|
-
import { extractTextFromPdf, hasSourceText } from "./pdfUtils.js";
|
|
4
|
+
import { extractTextFromPdf, hasSourceText, rasterizePage } from "./pdfUtils.js";
|
|
7
5
|
let pdfLib = null;
|
|
8
6
|
async function getPdfLib() {
|
|
9
7
|
if (!pdfLib) {
|
|
@@ -105,7 +103,7 @@ async function compressPagesWithQuality(pdfData, extractedPdfInfo, imageQuality,
|
|
|
105
103
|
for (let i = 0; i < extractedPdfInfo.pages.length; i++) {
|
|
106
104
|
const page = pdfDoc.getPages()[i];
|
|
107
105
|
const rasterizedPage = await rasterizePage(pdfData, i + 1, imageQuality);
|
|
108
|
-
const compressedImage = await compressImage(
|
|
106
|
+
const compressedImage = await compressImage(rasterizedPage, imageQuality);
|
|
109
107
|
if (!disableSourceText) {
|
|
110
108
|
await addTextToPdfPage(page, extractedText);
|
|
111
109
|
}
|
|
@@ -187,42 +185,6 @@ async function getFontFromName(fontName) {
|
|
|
187
185
|
}
|
|
188
186
|
return font;
|
|
189
187
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Rasterizes a PDF page.
|
|
192
|
-
*
|
|
193
|
-
* @param pdfData Buffer representation of the entire PDF file.
|
|
194
|
-
* @param index Index of the page to rasterize.
|
|
195
|
-
* @param quality Quality to apply during rasterization.
|
|
196
|
-
*/
|
|
197
|
-
async function rasterizePage(pdfData, index, quality = 85) {
|
|
198
|
-
const popplerImport = await loadOptionalDependency("node-poppler", "Image Processing");
|
|
199
|
-
const poppler = popplerImport.default || popplerImport;
|
|
200
|
-
const popplerInstance = new poppler.Poppler();
|
|
201
|
-
const tmpPdf = tmp.fileSync();
|
|
202
|
-
const tempPdfPath = tmpPdf.name;
|
|
203
|
-
const antialiasOption = "best";
|
|
204
|
-
try {
|
|
205
|
-
await fs.promises.writeFile(tempPdfPath, pdfData);
|
|
206
|
-
const options = {
|
|
207
|
-
antialias: antialiasOption,
|
|
208
|
-
firstPageToConvert: index,
|
|
209
|
-
lastPageToConvert: index,
|
|
210
|
-
jpegFile: true,
|
|
211
|
-
jpegOptions: `quality=${quality}`,
|
|
212
|
-
singleFile: true
|
|
213
|
-
};
|
|
214
|
-
const jpegBuffer = await popplerInstance.pdfToCairo(tempPdfPath, undefined, options);
|
|
215
|
-
await fs.promises.unlink(tempPdfPath);
|
|
216
|
-
return jpegBuffer;
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
logger.error("Error rasterizing PDF:", error);
|
|
220
|
-
throw error;
|
|
221
|
-
}
|
|
222
|
-
finally {
|
|
223
|
-
tmpPdf.removeCallback();
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
188
|
/**
|
|
227
189
|
* Performs linear interpolation between two numbers.
|
|
228
190
|
* @param start The starting value.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { LocalInputSource } from "../input/index.js";
|
|
2
|
+
import { ExtractedPdf } from "../pdf/extractedPdf.js";
|
|
3
|
+
export declare class PdfExtractor {
|
|
4
|
+
/**
|
|
5
|
+
* Buffer containing the PDF data.
|
|
6
|
+
* @private
|
|
7
|
+
*/
|
|
8
|
+
private sourcePdf;
|
|
9
|
+
/**
|
|
10
|
+
* Filename of the PDF.
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
private filename;
|
|
14
|
+
/**
|
|
15
|
+
* Input document.
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
private readonly inputDocument;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the extractor has been initialized.
|
|
21
|
+
* @private
|
|
22
|
+
*/
|
|
23
|
+
private initialized;
|
|
24
|
+
/**
|
|
25
|
+
* PDF library instance.
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
private pdfLib;
|
|
29
|
+
/**
|
|
30
|
+
* List of extracted PDFs.
|
|
31
|
+
* @private
|
|
32
|
+
*/
|
|
33
|
+
private extractedPdfs;
|
|
34
|
+
constructor(inputDocument: string | LocalInputSource);
|
|
35
|
+
init(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Gets the number of pages in the PDF.
|
|
38
|
+
* @returns The number of pages in the PDF.
|
|
39
|
+
*/
|
|
40
|
+
getPageCount(): Promise<number>;
|
|
41
|
+
/**
|
|
42
|
+
* Extracts pages from the PDF.
|
|
43
|
+
* @param pageIndexes
|
|
44
|
+
*/
|
|
45
|
+
extractSubDocuments(pageIndexes: number[][]): Promise<ExtractedPdf[]>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { PageOptionsOperation, PathInput } from "../input/index.js";
|
|
2
|
+
import { logger } from "../logger.js";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { loadOptionalDependency } from "../dependency/index.js";
|
|
5
|
+
import { MindeeInputSourceError, MindeePdfError } from "../errors/index.js";
|
|
6
|
+
import { ExtractedPdf } from "../pdf/extractedPdf.js";
|
|
7
|
+
import { createPdfFromInputSource, extractPages } from "../pdf/pdfOperation.js";
|
|
8
|
+
let pdfLib = null;
|
|
9
|
+
async function getPdfLib() {
|
|
10
|
+
if (!pdfLib) {
|
|
11
|
+
const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding");
|
|
12
|
+
pdfLib = pdfLibImport.default || pdfLibImport;
|
|
13
|
+
}
|
|
14
|
+
return pdfLib;
|
|
15
|
+
}
|
|
16
|
+
export class PdfExtractor {
|
|
17
|
+
constructor(inputDocument) {
|
|
18
|
+
/**
|
|
19
|
+
* Buffer containing the PDF data.
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
this.sourcePdf = null;
|
|
23
|
+
/**
|
|
24
|
+
* Filename of the PDF.
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
this.filename = null;
|
|
28
|
+
/**
|
|
29
|
+
* Whether the extractor has been initialized.
|
|
30
|
+
* @private
|
|
31
|
+
*/
|
|
32
|
+
this.initialized = false;
|
|
33
|
+
/**
|
|
34
|
+
* PDF library instance.
|
|
35
|
+
* @private
|
|
36
|
+
*/
|
|
37
|
+
this.pdfLib = null;
|
|
38
|
+
/**
|
|
39
|
+
* List of extracted PDFs.
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
this.extractedPdfs = null;
|
|
43
|
+
this.inputDocument = inputDocument;
|
|
44
|
+
}
|
|
45
|
+
async init() {
|
|
46
|
+
this.pdfLib = await getPdfLib();
|
|
47
|
+
if (typeof this.inputDocument === "string") {
|
|
48
|
+
logger.debug(`Loading from path: ${this.inputDocument}`);
|
|
49
|
+
try {
|
|
50
|
+
const tempPathInput = new PathInput({ inputPath: this.inputDocument });
|
|
51
|
+
await tempPathInput.init();
|
|
52
|
+
if (tempPathInput.isPdf()) {
|
|
53
|
+
this.sourcePdf = tempPathInput.fileObject;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const pdfObject = await createPdfFromInputSource(tempPathInput);
|
|
57
|
+
this.sourcePdf = Buffer.from(await pdfObject.save());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
throw new MindeeInputSourceError("Couldn't generate PDF from input.");
|
|
62
|
+
}
|
|
63
|
+
this.filename = path.basename(this.inputDocument);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
logger.debug(`Loading document: ${this.inputDocument.filename}`);
|
|
67
|
+
await this.inputDocument.init();
|
|
68
|
+
if (this.inputDocument.isPdf()) {
|
|
69
|
+
this.sourcePdf = this.inputDocument.fileObject;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const pdfObject = await createPdfFromInputSource(this.inputDocument);
|
|
73
|
+
const arrayBuffer = await pdfObject.save();
|
|
74
|
+
this.sourcePdf = Buffer.from(arrayBuffer);
|
|
75
|
+
}
|
|
76
|
+
this.filename = this.inputDocument.filename;
|
|
77
|
+
}
|
|
78
|
+
this.initialized = true;
|
|
79
|
+
if (!this.sourcePdf) {
|
|
80
|
+
throw new MindeePdfError("Could not load PDF source.");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Gets the number of pages in the PDF.
|
|
85
|
+
* @returns The number of pages in the PDF.
|
|
86
|
+
*/
|
|
87
|
+
async getPageCount() {
|
|
88
|
+
if (!this.initialized) {
|
|
89
|
+
await this.init();
|
|
90
|
+
}
|
|
91
|
+
const currentPdf = await this.pdfLib.PDFDocument.load(this.sourcePdf, {
|
|
92
|
+
ignoreEncryption: true,
|
|
93
|
+
password: ""
|
|
94
|
+
});
|
|
95
|
+
return currentPdf.getPageCount();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Extracts pages from the PDF.
|
|
99
|
+
* @param pageIndexes
|
|
100
|
+
*/
|
|
101
|
+
async extractSubDocuments(pageIndexes) {
|
|
102
|
+
if (this.extractedPdfs && this.extractedPdfs.length > 0) {
|
|
103
|
+
return this.extractedPdfs;
|
|
104
|
+
}
|
|
105
|
+
if (!this.initialized) {
|
|
106
|
+
await this.init();
|
|
107
|
+
}
|
|
108
|
+
this.extractedPdfs = [];
|
|
109
|
+
for (const pageRange of pageIndexes) {
|
|
110
|
+
logger.debug(`Extracting pages ${pageRange.join(", ")}`);
|
|
111
|
+
if (pageRange.length === 0) {
|
|
112
|
+
throw new MindeeInputSourceError("Empty indexes not allowed for extraction.");
|
|
113
|
+
}
|
|
114
|
+
const pageOptions = {
|
|
115
|
+
pageIndexes: pageRange,
|
|
116
|
+
operation: PageOptionsOperation.KeepOnly,
|
|
117
|
+
onMinPages: 1,
|
|
118
|
+
};
|
|
119
|
+
const splitName = path.basename(this.filename, path.extname(this.filename));
|
|
120
|
+
const startPage = String(pageRange[0] + 1).padStart(3, "0");
|
|
121
|
+
const endPage = String(pageRange[pageRange.length - 1] + 1).padStart(3, "0");
|
|
122
|
+
const fieldFilename = `${splitName}_page_${startPage}-${endPage}.pdf`;
|
|
123
|
+
const page = await extractPages(this.sourcePdf, pageOptions);
|
|
124
|
+
this.extractedPdfs.push(new ExtractedPdf(page.file, fieldFilename, pageRange.length));
|
|
125
|
+
}
|
|
126
|
+
return this.extractedPdfs;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type * as pdfLibTypes from "@cantoo/pdf-lib";
|
|
1
2
|
import { PageOptions } from "../input/pageOptions.js";
|
|
3
|
+
import { LocalInputSource } from "../input/index.js";
|
|
2
4
|
export interface SplitPdf {
|
|
3
5
|
file: Buffer;
|
|
4
6
|
totalPagesRemoved: number;
|
|
@@ -16,3 +18,8 @@ export declare function extractPages(file: Buffer, pageOptions: PageOptions): Pr
|
|
|
16
18
|
* @returns the number of pages in the file.
|
|
17
19
|
*/
|
|
18
20
|
export declare function countPages(file: Buffer): Promise<number>;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a PDF from a local file. Converts images to PDFs if needed.
|
|
23
|
+
* @param inputSource The input source to create a PDF from.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createPdfFromInputSource(inputSource: LocalInputSource): Promise<pdfLibTypes.PDFDocument>;
|
package/src/pdf/pdfOperation.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { errorHandler } from "../errors/handler.js";
|
|
2
2
|
import { PageOptionsOperation } from "../input/pageOptions.js";
|
|
3
|
-
import { MindeeError } from "../errors/index.js";
|
|
3
|
+
import { MindeeError, MindeeInputSourceError } from "../errors/index.js";
|
|
4
4
|
import { logger } from "../logger.js";
|
|
5
5
|
import { loadOptionalDependency } from "../dependency/index.js";
|
|
6
6
|
let pdfLib = null;
|
|
@@ -84,3 +84,36 @@ export async function countPages(file) {
|
|
|
84
84
|
});
|
|
85
85
|
return currentPdf.getPageCount();
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates a PDF from a local file. Converts images to PDFs if needed.
|
|
89
|
+
* @param inputSource The input source to create a PDF from.
|
|
90
|
+
*/
|
|
91
|
+
export async function createPdfFromInputSource(inputSource) {
|
|
92
|
+
const pdfLib = await getPdfLib();
|
|
93
|
+
let pdfDoc;
|
|
94
|
+
if (!["image/jpeg", "image/jpg", "image/png", "application/pdf"].includes(inputSource.mimeType)) {
|
|
95
|
+
throw new MindeeInputSourceError('Unsupported file type "' +
|
|
96
|
+
inputSource.mimeType +
|
|
97
|
+
'" Currently supported types are .png, .jpg and .pdf');
|
|
98
|
+
}
|
|
99
|
+
else if (inputSource.isPdf()) {
|
|
100
|
+
pdfDoc = await pdfLib.PDFDocument.load(inputSource.fileObject, {
|
|
101
|
+
ignoreEncryption: true,
|
|
102
|
+
password: ""
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
pdfDoc = await pdfLib.PDFDocument.create();
|
|
107
|
+
let image;
|
|
108
|
+
if (inputSource.mimeType === "image/png") {
|
|
109
|
+
image = await pdfDoc.embedPng(inputSource.fileObject);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
image = await pdfDoc.embedJpg(inputSource.fileObject);
|
|
113
|
+
}
|
|
114
|
+
const imageDims = image.scale(1);
|
|
115
|
+
const pageImage = pdfDoc.addPage([imageDims.width, imageDims.height]);
|
|
116
|
+
pageImage.drawImage(image);
|
|
117
|
+
}
|
|
118
|
+
return pdfDoc;
|
|
119
|
+
}
|
package/src/pdf/pdfUtils.d.ts
CHANGED
|
@@ -28,3 +28,12 @@ export declare function extractTextFromPdf(pdfBuffer: Buffer): Promise<Extracted
|
|
|
28
28
|
* @returns A Promise containing a boolean indicating if the PDF has source text.
|
|
29
29
|
*/
|
|
30
30
|
export declare function hasSourceText(pdfData: Buffer): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Rasterizes a PDF page.
|
|
33
|
+
*
|
|
34
|
+
* @param pdfData Buffer representation of the entire PDF file.
|
|
35
|
+
* @param index Index of the page to rasterize.
|
|
36
|
+
* @param quality Quality to apply during rasterization.
|
|
37
|
+
* @return Buffer containing the rasterized image data.
|
|
38
|
+
*/
|
|
39
|
+
export declare function rasterizePage(pdfData: Buffer, index: number, quality?: number): Promise<Buffer>;
|
package/src/pdf/pdfUtils.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import tmp from "tmp";
|
|
2
|
+
import * as fs from "node:fs";
|
|
1
3
|
import { MindeePdfError } from "../errors/index.js";
|
|
2
4
|
import { loadOptionalDependency } from "../dependency/index.js";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
3
6
|
function getConcatenatedText(pages) {
|
|
4
7
|
return pages.flatMap(page => page.content.map(item => item.str)).join(" ");
|
|
5
8
|
}
|
|
@@ -51,3 +54,40 @@ export async function hasSourceText(pdfData) {
|
|
|
51
54
|
const text = await extractTextFromPdf(pdfData);
|
|
52
55
|
return text.getConcatenatedText().trim().length > 0;
|
|
53
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Rasterizes a PDF page.
|
|
59
|
+
*
|
|
60
|
+
* @param pdfData Buffer representation of the entire PDF file.
|
|
61
|
+
* @param index Index of the page to rasterize.
|
|
62
|
+
* @param quality Quality to apply during rasterization.
|
|
63
|
+
* @return Buffer containing the rasterized image data.
|
|
64
|
+
*/
|
|
65
|
+
export async function rasterizePage(pdfData, index, quality = 85) {
|
|
66
|
+
const popplerImport = await loadOptionalDependency("node-poppler", "Image Processing");
|
|
67
|
+
const poppler = popplerImport.default || popplerImport;
|
|
68
|
+
const popplerInstance = new poppler.Poppler();
|
|
69
|
+
const tmpPdf = tmp.fileSync();
|
|
70
|
+
const tempPdfPath = tmpPdf.name;
|
|
71
|
+
const antialiasOption = "best";
|
|
72
|
+
try {
|
|
73
|
+
await fs.promises.writeFile(tempPdfPath, pdfData);
|
|
74
|
+
const options = {
|
|
75
|
+
antialias: antialiasOption,
|
|
76
|
+
firstPageToConvert: index,
|
|
77
|
+
lastPageToConvert: index,
|
|
78
|
+
jpegFile: true,
|
|
79
|
+
jpegOptions: `quality=${quality}`,
|
|
80
|
+
singleFile: true
|
|
81
|
+
};
|
|
82
|
+
const jpegBuffer = await popplerInstance.pdfToCairo(tempPdfPath, undefined, options);
|
|
83
|
+
await fs.promises.unlink(tempPdfPath);
|
|
84
|
+
return Buffer.from(jpegBuffer, "binary");
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
logger.error("Error rasterizing PDF:", error);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
tmpPdf.removeCallback();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { MindeeError
|
|
1
|
+
import { MindeeError } from "../../../errors/index.js";
|
|
2
|
+
import { createPdfFromInputSource } from "../../../pdf/pdfOperation.js";
|
|
2
3
|
import { ExtractedMultiReceiptImage } from "../../../v1/extraction/index.js";
|
|
3
4
|
import { extractFromPage } from "../../../image/index.js";
|
|
4
5
|
import { loadOptionalDependency } from "../../../dependency/index.js";
|
|
@@ -20,42 +21,14 @@ async function getPdfLib() {
|
|
|
20
21
|
* pages.
|
|
21
22
|
*/
|
|
22
23
|
async function extractReceiptsFromPage(pdfPage, boundingBoxes, pageId) {
|
|
23
|
-
const
|
|
24
|
+
const manualUpscaleFactor = 300 / 72;
|
|
25
|
+
const extractedReceiptsRaw = await extractFromPage(pdfPage, boundingBoxes, false, manualUpscaleFactor);
|
|
24
26
|
const extractedReceipts = [];
|
|
25
27
|
for (let i = 0; i < extractedReceiptsRaw.length; i++) {
|
|
26
28
|
extractedReceipts.push(new ExtractedMultiReceiptImage(extractedReceiptsRaw[i], pageId, i));
|
|
27
29
|
}
|
|
28
30
|
return extractedReceipts;
|
|
29
31
|
}
|
|
30
|
-
async function loadPdfDoc(inputFile) {
|
|
31
|
-
const pdfLib = await getPdfLib();
|
|
32
|
-
let pdfDoc;
|
|
33
|
-
if (!["image/jpeg", "image/jpg", "image/png", "application/pdf"].includes(inputFile.mimeType)) {
|
|
34
|
-
throw new MindeeInputSourceError('Unsupported file type "' +
|
|
35
|
-
inputFile.mimeType +
|
|
36
|
-
'" Currently supported types are .png, .jpg and .pdf');
|
|
37
|
-
}
|
|
38
|
-
else if (inputFile.isPdf()) {
|
|
39
|
-
pdfDoc = await pdfLib.PDFDocument.load(inputFile.fileObject, {
|
|
40
|
-
ignoreEncryption: true,
|
|
41
|
-
password: ""
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
pdfDoc = await pdfLib.PDFDocument.create();
|
|
46
|
-
let image;
|
|
47
|
-
if (inputFile.mimeType === "image/png") {
|
|
48
|
-
image = await pdfDoc.embedPng(inputFile.fileObject);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
image = await pdfDoc.embedJpg(inputFile.fileObject);
|
|
52
|
-
}
|
|
53
|
-
const imageDims = image.scale(1);
|
|
54
|
-
const pageImage = pdfDoc.addPage([imageDims.width, imageDims.height]);
|
|
55
|
-
pageImage.drawImage(image);
|
|
56
|
-
}
|
|
57
|
-
return pdfDoc;
|
|
58
|
-
}
|
|
59
32
|
/**
|
|
60
33
|
* Extracts individual receipts from multi-receipts documents.
|
|
61
34
|
*
|
|
@@ -69,9 +42,9 @@ export async function extractReceipts(inputFile, inference) {
|
|
|
69
42
|
if (!inference.prediction.receipts) {
|
|
70
43
|
throw new MindeeError("No possible receipts candidates found for MultiReceipts extraction.");
|
|
71
44
|
}
|
|
72
|
-
const pdfDoc = await
|
|
45
|
+
const pdfDoc = await createPdfFromInputSource(inputFile);
|
|
73
46
|
for (let pageId = 0; pageId < pdfDoc.getPageCount(); pageId++) {
|
|
74
|
-
const
|
|
47
|
+
const page = pdfDoc.getPage(pageId);
|
|
75
48
|
page.setRotation(pdfLib.degrees(inference.pages[pageId].orientation?.value ?? 0));
|
|
76
49
|
const receiptPositions = inference.pages[pageId].prediction.receipts.map((receipt) => receipt.boundingBox);
|
|
77
50
|
const extractedReceipts = await extractReceiptsFromPage(page, receiptPositions, pageId);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LocalInputSource } from "../../input/index.js";
|
|
2
|
+
import { CropItem } from "../../v2/product/crop/index.js";
|
|
3
|
+
import { CropFiles } from "../../v2/fileOperations/cropFiles.js";
|
|
4
|
+
import { ExtractedImage } from "../../image/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* Extracts a single specified crop from a given input source.
|
|
7
|
+
* @param inputSource Local input source.
|
|
8
|
+
* @param crop Crop to extract.
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractSingleCrop(inputSource: LocalInputSource, crop: CropItem): Promise<ExtractedImage>;
|
|
11
|
+
/**
|
|
12
|
+
* Extracts a list of crops from a document.
|
|
13
|
+
* @param inputSource Local input source.
|
|
14
|
+
* @param crops List of crops to extract.
|
|
15
|
+
* @param quality JPEG quality of extracted images.
|
|
16
|
+
* @return a list of extracted files, as a CropFiles object.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractCrops(inputSource: LocalInputSource, crops: CropItem[], quality?: number): Promise<CropFiles>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { MindeeError } from "../../errors/index.js";
|
|
2
|
+
import { extractImagesFromPolygon } from "../../image/imageExtractor.js";
|
|
3
|
+
import { CropFiles } from "../../v2/fileOperations/cropFiles.js";
|
|
4
|
+
import { logger } from "../../logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* Extracts a single specified crop from a given input source.
|
|
7
|
+
* @param inputSource Local input source.
|
|
8
|
+
* @param crop Crop to extract.
|
|
9
|
+
*/
|
|
10
|
+
export async function extractSingleCrop(inputSource, crop) {
|
|
11
|
+
return (await extractCrops(inputSource, [crop]))[0];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Extracts a list of crops from a document.
|
|
15
|
+
* @param inputSource Local input source.
|
|
16
|
+
* @param crops List of crops to extract.
|
|
17
|
+
* @param quality JPEG quality of extracted images.
|
|
18
|
+
* @return a list of extracted files, as a CropFiles object.
|
|
19
|
+
*/
|
|
20
|
+
export async function extractCrops(inputSource, crops, quality) {
|
|
21
|
+
if (crops.length === 0) {
|
|
22
|
+
throw new MindeeError("No crop indexes provided.");
|
|
23
|
+
}
|
|
24
|
+
logger.debug("Extracting crops: " + crops.join(", "));
|
|
25
|
+
const polygonsByPage = new Map();
|
|
26
|
+
for (const crop of crops) {
|
|
27
|
+
const pageId = crop.location.page;
|
|
28
|
+
if (!polygonsByPage.has(pageId)) {
|
|
29
|
+
polygonsByPage.set(pageId, []);
|
|
30
|
+
}
|
|
31
|
+
polygonsByPage.get(pageId).push(crop.location.polygon);
|
|
32
|
+
}
|
|
33
|
+
const extractedCrops = await extractImagesFromPolygon(inputSource, polygonsByPage, quality);
|
|
34
|
+
return new CropFiles(...extractedCrops);
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LocalInputSource } from "../../input/index.js";
|
|
2
|
+
import { SplitFiles } from "../../v2/fileOperations/splitFiles.js";
|
|
3
|
+
/**
|
|
4
|
+
* Extracts a single specified split from a
|
|
5
|
+
* @param inputSource
|
|
6
|
+
* @param split
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractSingleSplit(inputSource: LocalInputSource, split: number[]): Promise<SplitFiles>;
|
|
9
|
+
/**
|
|
10
|
+
* Extracts splits as complete PDFs from the document.
|
|
11
|
+
* @param inputSource Local input source.
|
|
12
|
+
* @param splits List of sub-lists of pages to keep.
|
|
13
|
+
* @return a list of extracted files.
|
|
14
|
+
* @throws MindeeError if no indexes are provided.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractSplits(inputSource: LocalInputSource, splits: number[][]): Promise<SplitFiles>;
|
|
17
|
+
/**
|
|
18
|
+
* Expands a range of pages into a list of page indexes.
|
|
19
|
+
* @param range start and end of the page range
|
|
20
|
+
*/
|
|
21
|
+
export declare function expandRange(range: [number, number]): number[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MindeeError } from "../../errors/index.js";
|
|
2
|
+
import { PdfExtractor } from "../../pdf/pdfExtractor.js";
|
|
3
|
+
import { SplitFiles } from "../../v2/fileOperations/splitFiles.js";
|
|
4
|
+
import { logger } from "../../logger.js";
|
|
5
|
+
import { ExtractedPdf } from "../../pdf/extractedPdf.js";
|
|
6
|
+
/**
|
|
7
|
+
* Extracts a single specified split from a
|
|
8
|
+
* @param inputSource
|
|
9
|
+
* @param split
|
|
10
|
+
*/
|
|
11
|
+
export async function extractSingleSplit(inputSource, split) {
|
|
12
|
+
return await extractSplits(inputSource, [split]);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Extracts splits as complete PDFs from the document.
|
|
16
|
+
* @param inputSource Local input source.
|
|
17
|
+
* @param splits List of sub-lists of pages to keep.
|
|
18
|
+
* @return a list of extracted files.
|
|
19
|
+
* @throws MindeeError if no indexes are provided.
|
|
20
|
+
*/
|
|
21
|
+
export async function extractSplits(inputSource, splits) {
|
|
22
|
+
const pageGroups = splits.filter(e => e.length > 0);
|
|
23
|
+
if (pageGroups.length === 0) {
|
|
24
|
+
throw new MindeeError("No valid split indexes provided.");
|
|
25
|
+
}
|
|
26
|
+
logger.debug("Extracting splits: " + splits.join(", "));
|
|
27
|
+
const pdfExtractor = new PdfExtractor(inputSource);
|
|
28
|
+
await pdfExtractor.init();
|
|
29
|
+
if (splits.length === 0) {
|
|
30
|
+
return new SplitFiles();
|
|
31
|
+
}
|
|
32
|
+
const pageCount = await pdfExtractor.getPageCount();
|
|
33
|
+
if (splits.length === 1 && splits[0].at(-1) === pageCount - 1) {
|
|
34
|
+
return new SplitFiles(new ExtractedPdf(inputSource.fileObject, inputSource.filename, pageCount));
|
|
35
|
+
}
|
|
36
|
+
const subDocuments = await pdfExtractor.extractSubDocuments(pageGroups);
|
|
37
|
+
return new SplitFiles(...subDocuments);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Expands a range of pages into a list of page indexes.
|
|
41
|
+
* @param range start and end of the page range
|
|
42
|
+
*/
|
|
43
|
+
export function expandRange(range) {
|
|
44
|
+
if (range[0] > range[1]) {
|
|
45
|
+
throw new MindeeError("Invalid page range provided.");
|
|
46
|
+
}
|
|
47
|
+
return Array.from({ length: range[1] - range[0] + 1 }, (_, i) => range[0] + i);
|
|
48
|
+
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { FieldLocation } from "../../../v2/parsing/inference/field/index.js";
|
|
2
2
|
import { StringDict } from "../../../parsing/index.js";
|
|
3
|
+
import { LocalInputSource } from "../../../input/index.js";
|
|
4
|
+
import { ExtractedImage } from "../../../image/index.js";
|
|
3
5
|
export declare class CropItem {
|
|
4
6
|
objectType: string;
|
|
5
7
|
location: FieldLocation;
|
|
6
8
|
constructor(serverResponse: StringDict);
|
|
7
9
|
toString(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Extracts a single crop from an input.
|
|
12
|
+
* @param inputSource The input file to extract from.
|
|
13
|
+
* @param quality Optional quality parameter for image extraction, default is undefined (full quality).
|
|
14
|
+
*/
|
|
15
|
+
extractFromFile(inputSource: LocalInputSource, quality?: number): Promise<ExtractedImage>;
|
|
8
16
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FieldLocation } from "../../../v2/parsing/inference/field/index.js";
|
|
2
|
+
import { extractCrops } from "../../../v2/fileOperations/crop.js";
|
|
2
3
|
export class CropItem {
|
|
3
4
|
constructor(serverResponse) {
|
|
4
5
|
this.objectType = serverResponse["object_type"];
|
|
@@ -7,4 +8,12 @@ export class CropItem {
|
|
|
7
8
|
toString() {
|
|
8
9
|
return `* :Location: ${this.location}\n :Object Type: ${this.objectType}`;
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Extracts a single crop from an input.
|
|
13
|
+
* @param inputSource The input file to extract from.
|
|
14
|
+
* @param quality Optional quality parameter for image extraction, default is undefined (full quality).
|
|
15
|
+
*/
|
|
16
|
+
async extractFromFile(inputSource, quality = 1) {
|
|
17
|
+
return (await extractCrops(inputSource, [this], quality))[0];
|
|
18
|
+
}
|
|
10
19
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { LocalInputSource } from "../../../input/index.js";
|
|
1
2
|
import { StringDict } from "../../../parsing/stringDict.js";
|
|
2
|
-
import {
|
|
3
|
+
import { CropFiles } from "../../../v2/fileOperations/cropFiles.js";
|
|
3
4
|
import { BaseResponse } from "../../../v2/parsing/index.js";
|
|
5
|
+
import { CropInference } from "./cropInference.js";
|
|
4
6
|
export declare class CropResponse extends BaseResponse {
|
|
5
7
|
/**
|
|
6
8
|
* Response for a crop utility inference.
|
|
@@ -10,4 +12,10 @@ export declare class CropResponse extends BaseResponse {
|
|
|
10
12
|
* @param serverResponse JSON response from the server.
|
|
11
13
|
*/
|
|
12
14
|
constructor(serverResponse: StringDict);
|
|
15
|
+
/**
|
|
16
|
+
* Extracts all crops from an input.
|
|
17
|
+
* @param inputSource The input file to extract from.
|
|
18
|
+
* @param quality Optional quality parameter for image extraction, default is undefined (full quality).
|
|
19
|
+
*/
|
|
20
|
+
extractFromFile(inputSource: LocalInputSource, quality?: number): Promise<CropFiles>;
|
|
13
21
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { extractCrops } from "../../../v2/fileOperations/crop.js";
|
|
2
2
|
import { BaseResponse } from "../../../v2/parsing/index.js";
|
|
3
|
+
import { CropInference } from "./cropInference.js";
|
|
3
4
|
export class CropResponse extends BaseResponse {
|
|
4
5
|
/**
|
|
5
6
|
* @param serverResponse JSON response from the server.
|
|
@@ -8,4 +9,12 @@ export class CropResponse extends BaseResponse {
|
|
|
8
9
|
super(serverResponse);
|
|
9
10
|
this.inference = new CropInference(serverResponse["inference"]);
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Extracts all crops from an input.
|
|
14
|
+
* @param inputSource The input file to extract from.
|
|
15
|
+
* @param quality Optional quality parameter for image extraction, default is undefined (full quality).
|
|
16
|
+
*/
|
|
17
|
+
async extractFromFile(inputSource, quality = 1) {
|
|
18
|
+
return await extractCrops(inputSource, this.inference.result.crops, quality);
|
|
19
|
+
}
|
|
11
20
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StringDict } from "../../../parsing/index.js";
|
|
2
|
+
import { LocalInputSource } from "../../../input/index.js";
|
|
2
3
|
/**
|
|
3
4
|
* Split inference result.
|
|
4
5
|
*/
|
|
@@ -14,4 +15,9 @@ export declare class SplitRange {
|
|
|
14
15
|
documentType: string;
|
|
15
16
|
constructor(serverResponse: StringDict);
|
|
16
17
|
toString(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Extracts a single split from the input file.
|
|
20
|
+
* @param inputSource The input file to extract from.
|
|
21
|
+
*/
|
|
22
|
+
extractFromFile(inputSource: LocalInputSource): Promise<import("../../../pdf/extractedPdf.js").ExtractedPdf>;
|
|
17
23
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { expandRange, extractSplits } from "../../../v2/fileOperations/split.js";
|
|
1
2
|
/**
|
|
2
3
|
* Split inference result.
|
|
3
4
|
*/
|
|
@@ -10,4 +11,12 @@ export class SplitRange {
|
|
|
10
11
|
const pageRange = this.pageRange.join(",");
|
|
11
12
|
return `* :Page Range: ${pageRange}\n :Document Type: ${this.documentType}`;
|
|
12
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Extracts a single split from the input file.
|
|
16
|
+
* @param inputSource The input file to extract from.
|
|
17
|
+
*/
|
|
18
|
+
async extractFromFile(inputSource) {
|
|
19
|
+
const pageRange = [expandRange(this.pageRange)];
|
|
20
|
+
return (await extractSplits(inputSource, pageRange))[0];
|
|
21
|
+
}
|
|
13
22
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { StringDict } from "../../../parsing/stringDict.js";
|
|
2
2
|
import { SplitInference } from "./splitInference.js";
|
|
3
3
|
import { BaseResponse } from "../../../v2/parsing/index.js";
|
|
4
|
+
import { LocalInputSource } from "../../../input/index.js";
|
|
5
|
+
import { SplitFiles } from "../../../v2/fileOperations/splitFiles.js";
|
|
4
6
|
export declare class SplitResponse extends BaseResponse {
|
|
5
7
|
/**
|
|
6
8
|
* Response for an OCR utility inference.
|
|
@@ -10,4 +12,9 @@ export declare class SplitResponse extends BaseResponse {
|
|
|
10
12
|
* @param serverResponse JSON response from the server.
|
|
11
13
|
*/
|
|
12
14
|
constructor(serverResponse: StringDict);
|
|
15
|
+
/**
|
|
16
|
+
* Extracts all splits from an input PDF.
|
|
17
|
+
* @param inputSource The input file to extract from.
|
|
18
|
+
*/
|
|
19
|
+
extractFromFile(inputSource: LocalInputSource): Promise<SplitFiles>;
|
|
13
20
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SplitInference } from "./splitInference.js";
|
|
2
2
|
import { BaseResponse } from "../../../v2/parsing/index.js";
|
|
3
|
+
import { expandRange, extractSplits } from "../../../v2/fileOperations/split.js";
|
|
3
4
|
export class SplitResponse extends BaseResponse {
|
|
4
5
|
/**
|
|
5
6
|
* @param serverResponse JSON response from the server.
|
|
@@ -8,4 +9,15 @@ export class SplitResponse extends BaseResponse {
|
|
|
8
9
|
super(serverResponse);
|
|
9
10
|
this.inference = new SplitInference(serverResponse["inference"]);
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Extracts all splits from an input PDF.
|
|
14
|
+
* @param inputSource The input file to extract from.
|
|
15
|
+
*/
|
|
16
|
+
async extractFromFile(inputSource) {
|
|
17
|
+
const splits = [];
|
|
18
|
+
for (const split of this.inference.result.splits) {
|
|
19
|
+
splits.push(expandRange(split.pageRange));
|
|
20
|
+
}
|
|
21
|
+
return await extractSplits(inputSource, splits);
|
|
22
|
+
}
|
|
11
23
|
}
|