formatferry 0.1.3 → 1.0.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/dist/commands/auth.d.ts +17 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +102 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/batch.d.ts +10 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +92 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/convert.d.ts +26 -0
- package/dist/commands/convert.d.ts.map +1 -0
- package/dist/commands/convert.js +198 -0
- package/dist/commands/convert.js.map +1 -0
- package/dist/config-store.d.ts +27 -0
- package/dist/config-store.d.ts.map +1 -0
- package/dist/config-store.js +58 -0
- package/dist/config-store.js.map +1 -0
- package/dist/entitlement.d.ts +11 -0
- package/dist/entitlement.d.ts.map +1 -0
- package/dist/entitlement.js +91 -0
- package/dist/entitlement.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -11
- package/dist/index.js.map +1 -0
- package/dist/packages/cli/src/commands/auth.d.ts +17 -0
- package/dist/packages/cli/src/commands/auth.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/auth.js +102 -0
- package/dist/packages/cli/src/commands/auth.js.map +1 -0
- package/dist/packages/cli/src/commands/batch.d.ts +10 -0
- package/dist/packages/cli/src/commands/batch.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/batch.js +92 -0
- package/dist/packages/cli/src/commands/batch.js.map +1 -0
- package/dist/packages/cli/src/commands/convert.d.ts +26 -0
- package/dist/packages/cli/src/commands/convert.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/convert.js +198 -0
- package/dist/packages/cli/src/commands/convert.js.map +1 -0
- package/dist/packages/cli/src/config-store.d.ts +27 -0
- package/dist/packages/cli/src/config-store.d.ts.map +1 -0
- package/dist/packages/cli/src/config-store.js +58 -0
- package/dist/packages/cli/src/config-store.js.map +1 -0
- package/dist/packages/cli/src/entitlement.d.ts +11 -0
- package/dist/packages/cli/src/entitlement.d.ts.map +1 -0
- package/dist/packages/cli/src/entitlement.js +91 -0
- package/dist/packages/cli/src/entitlement.js.map +1 -0
- package/dist/packages/cli/src/index.d.ts +2 -0
- package/dist/packages/cli/src/index.d.ts.map +1 -0
- package/dist/packages/cli/src/index.js +28 -0
- package/dist/packages/cli/src/index.js.map +1 -0
- package/dist/packages/cli/src/shim.d.ts +2 -0
- package/dist/packages/cli/src/shim.d.ts.map +1 -0
- package/dist/packages/cli/src/shim.js +21 -0
- package/dist/packages/cli/src/shim.js.map +1 -0
- package/dist/shim.d.ts +2 -0
- package/dist/shim.d.ts.map +1 -0
- package/dist/shim.js +21 -0
- package/dist/shim.js.map +1 -0
- package/dist/src/shared/adapters/docx-adapter.d.ts +85 -0
- package/dist/src/shared/adapters/docx-adapter.d.ts.map +1 -0
- package/dist/src/shared/adapters/docx-adapter.js +164 -0
- package/dist/src/shared/adapters/docx-adapter.js.map +1 -0
- package/dist/src/shared/adapters/pdf-adapter.d.ts +94 -0
- package/dist/src/shared/adapters/pdf-adapter.d.ts.map +1 -0
- package/dist/src/shared/adapters/pdf-adapter.js +322 -0
- package/dist/src/shared/adapters/pdf-adapter.js.map +1 -0
- package/dist/src/shared/adapters/spreadsheet-adapter.d.ts +98 -0
- package/dist/src/shared/adapters/spreadsheet-adapter.d.ts.map +1 -0
- package/dist/src/shared/adapters/spreadsheet-adapter.js +424 -0
- package/dist/src/shared/adapters/spreadsheet-adapter.js.map +1 -0
- package/dist/src/shared/atlassian-detector.d.ts +6 -0
- package/dist/src/shared/atlassian-detector.d.ts.map +1 -0
- package/dist/src/shared/atlassian-detector.js +23 -0
- package/dist/src/shared/atlassian-detector.js.map +1 -0
- package/dist/src/shared/conversion-engine.d.ts +114 -0
- package/dist/src/shared/conversion-engine.d.ts.map +1 -0
- package/dist/src/shared/conversion-engine.js +790 -0
- package/dist/src/shared/conversion-engine.js.map +1 -0
- package/dist/src/shared/entitlement-service.d.ts +42 -0
- package/dist/src/shared/entitlement-service.d.ts.map +1 -0
- package/dist/src/shared/entitlement-service.js +38 -0
- package/dist/src/shared/entitlement-service.js.map +1 -0
- package/dist/src/shared/input-sanitizer.d.ts +93 -0
- package/dist/src/shared/input-sanitizer.d.ts.map +1 -0
- package/dist/src/shared/input-sanitizer.js +527 -0
- package/dist/src/shared/input-sanitizer.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Adapter Module
|
|
3
|
+
*
|
|
4
|
+
* Converts DOCX files to HTML using Mammoth.js for processing through
|
|
5
|
+
* the existing ConversionEngine pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 1.1, 1.3, 1.4, 1.5, 1.6
|
|
8
|
+
*/
|
|
9
|
+
import { loadLibrary } from '@/shared/dynamic-loader';
|
|
10
|
+
/** Error thrown when DOCX parsing fails */
|
|
11
|
+
export class DocxParseError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
originalError;
|
|
14
|
+
constructor(message, code, originalError) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.originalError = originalError;
|
|
18
|
+
this.name = 'DocxParseError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Counter for generating unique image IDs
|
|
23
|
+
*/
|
|
24
|
+
let imageCounter = 0;
|
|
25
|
+
/**
|
|
26
|
+
* Reset image counter (primarily for testing)
|
|
27
|
+
*/
|
|
28
|
+
export function resetImageCounter() {
|
|
29
|
+
imageCounter = 0;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* DOCX Adapter class
|
|
33
|
+
*
|
|
34
|
+
* Handles conversion of DOCX files to HTML using Mammoth.js.
|
|
35
|
+
* Images are converted to placeholder text rather than base64 data
|
|
36
|
+
* to keep output clean and avoid bloating the markdown.
|
|
37
|
+
*/
|
|
38
|
+
export class DocxAdapter {
|
|
39
|
+
mammoth = null;
|
|
40
|
+
/**
|
|
41
|
+
* Load the Mammoth library if not already loaded
|
|
42
|
+
*/
|
|
43
|
+
async ensureMammothLoaded() {
|
|
44
|
+
if (!this.mammoth) {
|
|
45
|
+
this.mammoth = await loadLibrary('mammoth');
|
|
46
|
+
}
|
|
47
|
+
return this.mammoth;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parse a DOCX file and convert it to HTML
|
|
51
|
+
*
|
|
52
|
+
* @param arrayBuffer - The DOCX file content as an ArrayBuffer
|
|
53
|
+
* @returns Promise resolving to the parse result with HTML and metadata
|
|
54
|
+
* @throws DocxParseError if the file cannot be parsed
|
|
55
|
+
*/
|
|
56
|
+
async parseToHtml(arrayBuffer) {
|
|
57
|
+
const mammoth = await this.ensureMammothLoaded();
|
|
58
|
+
const images = [];
|
|
59
|
+
const warnings = [];
|
|
60
|
+
try {
|
|
61
|
+
// Custom image converter that creates placeholder text instead of base64
|
|
62
|
+
// This satisfies Requirement 1.5: no base64 embedding
|
|
63
|
+
const imageConverter = {
|
|
64
|
+
__mammothType: 'image-converter',
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
_convert: async (image) => {
|
|
67
|
+
imageCounter++;
|
|
68
|
+
const imageId = imageCounter;
|
|
69
|
+
// Extract image information
|
|
70
|
+
const contentType = image.contentType || 'image/unknown';
|
|
71
|
+
const altText = image.altText || '';
|
|
72
|
+
// Generate a filename based on content type
|
|
73
|
+
const extension = contentType.split('/')[1] || 'bin';
|
|
74
|
+
const filename = `image-${imageId}.${extension}`;
|
|
75
|
+
// Store image reference
|
|
76
|
+
const imageRef = {
|
|
77
|
+
filename,
|
|
78
|
+
contentType,
|
|
79
|
+
altText: altText || undefined,
|
|
80
|
+
};
|
|
81
|
+
images.push(imageRef);
|
|
82
|
+
// Return a placeholder src that will be converted to text
|
|
83
|
+
// Using a data URI with a special marker that we'll replace
|
|
84
|
+
return { src: `[IMAGE: ${filename}${altText ? ` - ${altText}` : ''}]` };
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
// Convert DOCX to HTML using Mammoth
|
|
88
|
+
// Mammoth handles: headings, bold, italic, lists, tables (Req 1.3, 1.4, 1.6)
|
|
89
|
+
const result = await mammoth.convertToHtml({
|
|
90
|
+
arrayBuffer,
|
|
91
|
+
}, {
|
|
92
|
+
// Use custom image converter to avoid base64 embedding
|
|
93
|
+
convertImage: imageConverter,
|
|
94
|
+
});
|
|
95
|
+
// Process Mammoth messages into warnings
|
|
96
|
+
if (result.messages && Array.isArray(result.messages)) {
|
|
97
|
+
for (const msg of result.messages) {
|
|
98
|
+
if (msg && typeof msg === 'object' && 'message' in msg) {
|
|
99
|
+
const message = msg.message;
|
|
100
|
+
const type = msg.type;
|
|
101
|
+
if (type === 'warning' || type === 'error') {
|
|
102
|
+
warnings.push(message);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Post-process HTML to convert image placeholders to proper text
|
|
108
|
+
let html = result.value || '';
|
|
109
|
+
// Replace img tags with placeholder src to plain text placeholders
|
|
110
|
+
html = html.replace(/<img[^>]*src="(\[IMAGE:[^\]]+\])"[^>]*>/gi, (_, placeholder) => `<p><em>${placeholder}</em></p>`);
|
|
111
|
+
return {
|
|
112
|
+
html,
|
|
113
|
+
images,
|
|
114
|
+
metadata: {
|
|
115
|
+
imageCount: images.length,
|
|
116
|
+
},
|
|
117
|
+
warnings,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
// Handle specific Mammoth errors
|
|
122
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
123
|
+
// Check for format issues first (more specific)
|
|
124
|
+
if (errorMessage.includes('not a valid') ||
|
|
125
|
+
errorMessage.includes('not a zip') ||
|
|
126
|
+
errorMessage.includes('missing required') ||
|
|
127
|
+
errorMessage.includes('missing file')) {
|
|
128
|
+
throw new DocxParseError('This file does not appear to be a valid DOCX document.', 'INVALID_FORMAT', error instanceof Error ? error : undefined);
|
|
129
|
+
}
|
|
130
|
+
// Check for common corruption indicators
|
|
131
|
+
if (errorMessage.includes('could not find') ||
|
|
132
|
+
errorMessage.includes('invalid') ||
|
|
133
|
+
errorMessage.includes('unexpected') ||
|
|
134
|
+
errorMessage.includes('corrupted') ||
|
|
135
|
+
errorMessage.includes('end of data')) {
|
|
136
|
+
throw new DocxParseError('Unable to read this file. It may be corrupted. Try opening it in Word first to verify it\'s valid.', 'CORRUPTED', error instanceof Error ? error : undefined);
|
|
137
|
+
}
|
|
138
|
+
// Generic parse error
|
|
139
|
+
throw new DocxParseError(`Failed to parse DOCX file: ${error instanceof Error ? error.message : String(error)}`, 'PARSE_ERROR', error instanceof Error ? error : undefined);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract raw text from a DOCX file (without formatting)
|
|
144
|
+
*
|
|
145
|
+
* @param arrayBuffer - The DOCX file content as an ArrayBuffer
|
|
146
|
+
* @returns Promise resolving to the raw text content
|
|
147
|
+
*/
|
|
148
|
+
async extractRawText(arrayBuffer) {
|
|
149
|
+
const mammoth = await this.ensureMammothLoaded();
|
|
150
|
+
try {
|
|
151
|
+
const result = await mammoth.extractRawText({ arrayBuffer });
|
|
152
|
+
return result.value || '';
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
156
|
+
throw new DocxParseError(`Failed to extract text from DOCX: ${errorMessage}`, 'PARSE_ERROR', error instanceof Error ? error : undefined);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Singleton instance for convenience
|
|
162
|
+
*/
|
|
163
|
+
export const docxAdapter = new DocxAdapter();
|
|
164
|
+
//# sourceMappingURL=docx-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docx-adapter.js","sourceRoot":"","sources":["../../../../../../src/shared/adapters/docx-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF Adapter Module
|
|
3
|
+
*
|
|
4
|
+
* Converts PDF files to text/HTML using PDF.js for processing through
|
|
5
|
+
* the existing ConversionEngine pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8
|
|
8
|
+
*/
|
|
9
|
+
/** Page information extracted from PDF */
|
|
10
|
+
export interface PdfPageInfo {
|
|
11
|
+
/** Page number (1-indexed) */
|
|
12
|
+
pageNumber: number;
|
|
13
|
+
/** Text content extracted from the page */
|
|
14
|
+
textContent: string;
|
|
15
|
+
/** Whether the page contains images */
|
|
16
|
+
hasImages: boolean;
|
|
17
|
+
/** Viewport width for multi-column detection */
|
|
18
|
+
viewportWidth?: number;
|
|
19
|
+
}
|
|
20
|
+
/** PDF metadata extracted during parsing */
|
|
21
|
+
export interface PdfMetadata {
|
|
22
|
+
/** Document title (if available) */
|
|
23
|
+
title?: string;
|
|
24
|
+
/** Document author (if available) */
|
|
25
|
+
author?: string;
|
|
26
|
+
/** Total number of pages */
|
|
27
|
+
pageCount: number;
|
|
28
|
+
/** Whether the PDF is text-based (has text layer) */
|
|
29
|
+
isTextBased: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Result of parsing a PDF file */
|
|
32
|
+
export interface PdfParseResult {
|
|
33
|
+
/** HTML content with page markers */
|
|
34
|
+
html: string;
|
|
35
|
+
/** Information about each page */
|
|
36
|
+
pages: PdfPageInfo[];
|
|
37
|
+
/** Document metadata */
|
|
38
|
+
metadata: PdfMetadata;
|
|
39
|
+
/** Whether the PDF is text-based */
|
|
40
|
+
isTextBased: boolean;
|
|
41
|
+
/** Any warnings generated during parsing */
|
|
42
|
+
warnings: string[];
|
|
43
|
+
}
|
|
44
|
+
/** Error thrown when PDF parsing fails */
|
|
45
|
+
export declare class PdfParseError extends Error {
|
|
46
|
+
readonly code: 'CORRUPTED' | 'PASSWORD_PROTECTED' | 'IMAGE_ONLY' | 'PARSE_ERROR';
|
|
47
|
+
readonly originalError?: Error | undefined;
|
|
48
|
+
constructor(message: string, code: 'CORRUPTED' | 'PASSWORD_PROTECTED' | 'IMAGE_ONLY' | 'PARSE_ERROR', originalError?: Error | undefined);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* PDF Adapter class
|
|
52
|
+
*
|
|
53
|
+
* Handles conversion of PDF files to HTML using PDF.js.
|
|
54
|
+
* Extracts text content and adds page markers between pages.
|
|
55
|
+
*/
|
|
56
|
+
export declare class PdfAdapter {
|
|
57
|
+
private pdfjs;
|
|
58
|
+
/**
|
|
59
|
+
* Load the PDF.js library if not already loaded
|
|
60
|
+
*/
|
|
61
|
+
private ensurePdfjsLoaded;
|
|
62
|
+
/**
|
|
63
|
+
* Parse a PDF file and convert it to HTML with page markers
|
|
64
|
+
*
|
|
65
|
+
* @param arrayBuffer - The PDF file content as an ArrayBuffer
|
|
66
|
+
* @param onProgress - Optional progress callback (0-1)
|
|
67
|
+
* @returns Promise resolving to the parse result with HTML and metadata
|
|
68
|
+
* @throws PdfParseError if the file cannot be parsed
|
|
69
|
+
*/
|
|
70
|
+
parseToText(arrayBuffer: ArrayBuffer, onProgress?: (progress: number) => void): Promise<PdfParseResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Convert parsed pages to HTML with page markers
|
|
73
|
+
*
|
|
74
|
+
* Requirement 3.5: Insert page markers between pages
|
|
75
|
+
*
|
|
76
|
+
* Note: We use a <div> element with data-page-marker attribute instead of
|
|
77
|
+
* HTML comments because DOMPurify strips comments by default. The marker
|
|
78
|
+
* element survives sanitization and is converted to the comment format
|
|
79
|
+
* after sanitization in the file processor pipeline.
|
|
80
|
+
*/
|
|
81
|
+
private pagesToHtml;
|
|
82
|
+
/**
|
|
83
|
+
* Get the number of pages in a PDF without full parsing
|
|
84
|
+
*
|
|
85
|
+
* @param arrayBuffer - The PDF file content as an ArrayBuffer
|
|
86
|
+
* @returns Promise resolving to the page count
|
|
87
|
+
*/
|
|
88
|
+
getPageCount(arrayBuffer: ArrayBuffer): Promise<number>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Singleton instance for convenience
|
|
92
|
+
*/
|
|
93
|
+
export declare const pdfAdapter: PdfAdapter;
|
|
94
|
+
//# sourceMappingURL=pdf-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-adapter.d.ts","sourceRoot":"","sources":["../../../../../../src/shared/adapters/pdf-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,0CAA0C;AAC1C,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,wBAAwB;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,oCAAoC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,0CAA0C;AAC1C,qBAAa,aAAc,SAAQ,KAAK;aAGpB,IAAI,EAAE,WAAW,GAAG,oBAAoB,GAAG,YAAY,GAAG,aAAa;aACvE,aAAa,CAAC,EAAE,KAAK;gBAFrC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,WAAW,GAAG,oBAAoB,GAAG,YAAY,GAAG,aAAa,EACvE,aAAa,CAAC,EAAE,KAAK,YAAA;CAKxC;AA+DD;;;;;GAKG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAyB;IAEtC;;OAEG;YACW,iBAAiB;IAO/B;;;;;;;OAOG;IACG,WAAW,CACf,WAAW,EAAE,WAAW,EACxB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,cAAc,CAAC;IA6O1B;;;;;;;;;OASG;IACH,OAAO,CAAC,WAAW;IAgCnB;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;CAoB9D;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,YAAmB,CAAC"}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF Adapter Module
|
|
3
|
+
*
|
|
4
|
+
* Converts PDF files to text/HTML using PDF.js for processing through
|
|
5
|
+
* the existing ConversionEngine pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8
|
|
8
|
+
*/
|
|
9
|
+
import { loadLibrary } from '@/shared/dynamic-loader';
|
|
10
|
+
/** Error thrown when PDF parsing fails */
|
|
11
|
+
export class PdfParseError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
originalError;
|
|
14
|
+
constructor(message, code, originalError) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.originalError = originalError;
|
|
18
|
+
this.name = 'PdfParseError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Threshold for considering a PDF as image-only (minimum text characters) */
|
|
22
|
+
const MIN_TEXT_CHARS_THRESHOLD = 10;
|
|
23
|
+
/** Threshold for detecting multi-column layout (x-position variance) */
|
|
24
|
+
const MULTI_COLUMN_X_VARIANCE_THRESHOLD = 100;
|
|
25
|
+
/**
|
|
26
|
+
* Escape HTML special characters
|
|
27
|
+
*/
|
|
28
|
+
function escapeHtml(str) {
|
|
29
|
+
return str
|
|
30
|
+
.replace(/&/g, '&')
|
|
31
|
+
.replace(/</g, '<')
|
|
32
|
+
.replace(/>/g, '>')
|
|
33
|
+
.replace(/"/g, '"')
|
|
34
|
+
.replace(/'/g, ''');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* PDF Adapter class
|
|
38
|
+
*
|
|
39
|
+
* Handles conversion of PDF files to HTML using PDF.js.
|
|
40
|
+
* Extracts text content and adds page markers between pages.
|
|
41
|
+
*/
|
|
42
|
+
export class PdfAdapter {
|
|
43
|
+
pdfjs = null;
|
|
44
|
+
/**
|
|
45
|
+
* Load the PDF.js library if not already loaded
|
|
46
|
+
*/
|
|
47
|
+
async ensurePdfjsLoaded() {
|
|
48
|
+
if (!this.pdfjs) {
|
|
49
|
+
this.pdfjs = await loadLibrary('pdfjs');
|
|
50
|
+
}
|
|
51
|
+
return this.pdfjs;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse a PDF file and convert it to HTML with page markers
|
|
55
|
+
*
|
|
56
|
+
* @param arrayBuffer - The PDF file content as an ArrayBuffer
|
|
57
|
+
* @param onProgress - Optional progress callback (0-1)
|
|
58
|
+
* @returns Promise resolving to the parse result with HTML and metadata
|
|
59
|
+
* @throws PdfParseError if the file cannot be parsed
|
|
60
|
+
*/
|
|
61
|
+
async parseToText(arrayBuffer, onProgress) {
|
|
62
|
+
const pdfjs = await this.ensurePdfjsLoaded();
|
|
63
|
+
const warnings = [];
|
|
64
|
+
const pages = [];
|
|
65
|
+
let loadingTask = null;
|
|
66
|
+
try {
|
|
67
|
+
// Load the PDF document with JavaScript disabled (Req 3.13)
|
|
68
|
+
loadingTask = pdfjs.getDocument({
|
|
69
|
+
data: arrayBuffer,
|
|
70
|
+
disableAutoFetch: true,
|
|
71
|
+
disableStream: true,
|
|
72
|
+
// Disable JavaScript execution in PDF
|
|
73
|
+
isEvalSupported: false,
|
|
74
|
+
});
|
|
75
|
+
let pdf;
|
|
76
|
+
try {
|
|
77
|
+
pdf = await loadingTask.promise;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
// Check for password-protected PDF (Req 3.8)
|
|
81
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
82
|
+
if (errorMessage.includes('password') ||
|
|
83
|
+
errorMessage.includes('encrypted') ||
|
|
84
|
+
errorMessage.includes('need a password')) {
|
|
85
|
+
throw new PdfParseError('This PDF is password-protected and cannot be processed.', 'PASSWORD_PROTECTED', error instanceof Error ? error : undefined);
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
const numPages = pdf.numPages;
|
|
90
|
+
let totalTextLength = 0;
|
|
91
|
+
let hasMultiColumnWarning = false;
|
|
92
|
+
// Extract metadata
|
|
93
|
+
const metadata = {
|
|
94
|
+
pageCount: numPages,
|
|
95
|
+
isTextBased: false,
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
const pdfMetadata = await pdf.getMetadata();
|
|
99
|
+
if (pdfMetadata.info) {
|
|
100
|
+
metadata.title = pdfMetadata.info.Title;
|
|
101
|
+
metadata.author = pdfMetadata.info.Author;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Metadata extraction failed, continue without it
|
|
106
|
+
}
|
|
107
|
+
// Process each page
|
|
108
|
+
for (let pageNum = 1; pageNum <= numPages; pageNum++) {
|
|
109
|
+
onProgress?.(pageNum / numPages);
|
|
110
|
+
const page = await pdf.getPage(pageNum);
|
|
111
|
+
const viewport = page.getViewport({ scale: 1.0 });
|
|
112
|
+
// Get text content
|
|
113
|
+
const textContent = await page.getTextContent();
|
|
114
|
+
// Extract text items and check for multi-column layout
|
|
115
|
+
const textItems = [];
|
|
116
|
+
const xPositions = [];
|
|
117
|
+
for (const item of textContent.items) {
|
|
118
|
+
if ('str' in item && typeof item.str === 'string') {
|
|
119
|
+
textItems.push(item);
|
|
120
|
+
// Track x-positions for multi-column detection
|
|
121
|
+
if (item.transform && item.transform.length >= 5) {
|
|
122
|
+
xPositions.push(item.transform[4]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Detect multi-column layout (Req 3.3)
|
|
127
|
+
if (!hasMultiColumnWarning && xPositions.length > 10) {
|
|
128
|
+
const uniqueXPositions = [...new Set(xPositions.map(x => Math.round(x / 10) * 10))];
|
|
129
|
+
if (uniqueXPositions.length >= 3) {
|
|
130
|
+
const xRange = Math.max(...xPositions) - Math.min(...xPositions);
|
|
131
|
+
if (xRange > viewport.width * 0.5 && uniqueXPositions.length >= 3) {
|
|
132
|
+
// Check if there are distinct column groups
|
|
133
|
+
const sortedX = [...uniqueXPositions].sort((a, b) => a - b);
|
|
134
|
+
let hasGap = false;
|
|
135
|
+
for (let i = 1; i < sortedX.length; i++) {
|
|
136
|
+
if (sortedX[i] - sortedX[i - 1] > MULTI_COLUMN_X_VARIANCE_THRESHOLD) {
|
|
137
|
+
hasGap = true;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (hasGap) {
|
|
142
|
+
warnings.push('Multi-column PDFs may not preserve layout accurately');
|
|
143
|
+
hasMultiColumnWarning = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Build text content for the page
|
|
149
|
+
let pageText = '';
|
|
150
|
+
let lastY = null;
|
|
151
|
+
for (const item of textItems) {
|
|
152
|
+
const text = item.str;
|
|
153
|
+
if (!text)
|
|
154
|
+
continue;
|
|
155
|
+
// Check for line breaks based on y-position changes
|
|
156
|
+
if (item.transform && item.transform.length >= 6) {
|
|
157
|
+
const currentY = item.transform[5];
|
|
158
|
+
if (lastY !== null && Math.abs(currentY - lastY) > 5) {
|
|
159
|
+
// New line detected
|
|
160
|
+
pageText += '\n';
|
|
161
|
+
}
|
|
162
|
+
else if (pageText && !pageText.endsWith(' ') && !pageText.endsWith('\n')) {
|
|
163
|
+
// Add space between items on same line
|
|
164
|
+
pageText += ' ';
|
|
165
|
+
}
|
|
166
|
+
lastY = currentY;
|
|
167
|
+
}
|
|
168
|
+
else if (pageText && !pageText.endsWith(' ') && !pageText.endsWith('\n')) {
|
|
169
|
+
pageText += ' ';
|
|
170
|
+
}
|
|
171
|
+
pageText += text;
|
|
172
|
+
}
|
|
173
|
+
// Clean up the text
|
|
174
|
+
pageText = pageText
|
|
175
|
+
.replace(/\n{3,}/g, '\n\n') // Collapse multiple newlines
|
|
176
|
+
.replace(/[ \t]+/g, ' ') // Collapse multiple spaces
|
|
177
|
+
.trim();
|
|
178
|
+
totalTextLength += pageText.length;
|
|
179
|
+
// Check for images in the page (for hasImages flag)
|
|
180
|
+
let hasImages = false;
|
|
181
|
+
try {
|
|
182
|
+
const operatorList = await page.getOperatorList();
|
|
183
|
+
// OPS.paintImageXObject = 85, OPS.paintInlineImageXObject = 86
|
|
184
|
+
hasImages = operatorList.fnArray.some(op => op === 85 || op === 86);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// Operator list extraction failed, assume no images
|
|
188
|
+
}
|
|
189
|
+
pages.push({
|
|
190
|
+
pageNumber: pageNum,
|
|
191
|
+
textContent: pageText,
|
|
192
|
+
hasImages,
|
|
193
|
+
viewportWidth: viewport.width,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// Check if PDF is image-only (Req 3.7)
|
|
197
|
+
// A PDF is considered image-only if:
|
|
198
|
+
// 1. It has very little text AND contains images, OR
|
|
199
|
+
// 2. It has absolutely no text at all
|
|
200
|
+
// This avoids false positives for legitimate short-text PDFs (receipts, forms)
|
|
201
|
+
const hasAnyImages = pages.some(p => p.hasImages);
|
|
202
|
+
const hasNoText = totalTextLength === 0;
|
|
203
|
+
const hasMinimalTextWithImages = totalTextLength < MIN_TEXT_CHARS_THRESHOLD && hasAnyImages;
|
|
204
|
+
if (hasNoText || hasMinimalTextWithImages) {
|
|
205
|
+
throw new PdfParseError('This PDF contains only images. Text extraction requires a text layer. OCR is not supported.', 'IMAGE_ONLY');
|
|
206
|
+
}
|
|
207
|
+
metadata.isTextBased = true;
|
|
208
|
+
// Convert pages to HTML with page markers (Req 3.5)
|
|
209
|
+
const html = this.pagesToHtml(pages);
|
|
210
|
+
return {
|
|
211
|
+
html,
|
|
212
|
+
pages,
|
|
213
|
+
metadata,
|
|
214
|
+
isTextBased: true,
|
|
215
|
+
warnings,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
// Handle PdfParseError as-is
|
|
220
|
+
if (error instanceof PdfParseError) {
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
// Handle specific PDF.js errors
|
|
224
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
225
|
+
if (errorMessage.includes('password') ||
|
|
226
|
+
errorMessage.includes('encrypted')) {
|
|
227
|
+
throw new PdfParseError('This PDF is password-protected and cannot be processed.', 'PASSWORD_PROTECTED', error instanceof Error ? error : undefined);
|
|
228
|
+
}
|
|
229
|
+
if (errorMessage.includes('invalid pdf') ||
|
|
230
|
+
errorMessage.includes('not a pdf') ||
|
|
231
|
+
errorMessage.includes('missing pdf') ||
|
|
232
|
+
errorMessage.includes('corrupted') ||
|
|
233
|
+
errorMessage.includes('unexpected') ||
|
|
234
|
+
errorMessage.includes('invalid xref') ||
|
|
235
|
+
errorMessage.includes('bad xref')) {
|
|
236
|
+
throw new PdfParseError('Unable to read this PDF. It may be corrupted or in an unsupported format.', 'CORRUPTED', error instanceof Error ? error : undefined);
|
|
237
|
+
}
|
|
238
|
+
// Generic parse error
|
|
239
|
+
throw new PdfParseError(`Failed to parse PDF file: ${error instanceof Error ? error.message : String(error)}`, 'PARSE_ERROR', error instanceof Error ? error : undefined);
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
// Clean up loading task
|
|
243
|
+
if (loadingTask) {
|
|
244
|
+
try {
|
|
245
|
+
loadingTask.destroy();
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// Ignore cleanup errors
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Convert parsed pages to HTML with page markers
|
|
255
|
+
*
|
|
256
|
+
* Requirement 3.5: Insert page markers between pages
|
|
257
|
+
*
|
|
258
|
+
* Note: We use a <div> element with data-page-marker attribute instead of
|
|
259
|
+
* HTML comments because DOMPurify strips comments by default. The marker
|
|
260
|
+
* element survives sanitization and is converted to the comment format
|
|
261
|
+
* after sanitization in the file processor pipeline.
|
|
262
|
+
*/
|
|
263
|
+
pagesToHtml(pages) {
|
|
264
|
+
if (pages.length === 0) {
|
|
265
|
+
return '';
|
|
266
|
+
}
|
|
267
|
+
return pages.map((page, index) => {
|
|
268
|
+
// Add page marker for pages after the first (Req 3.5)
|
|
269
|
+
// Use a div with data attribute that survives DOMPurify sanitization
|
|
270
|
+
const pageMarker = index > 0
|
|
271
|
+
? `<div data-page-marker="${page.pageNumber}" class="pdf-page-break">--- Page ${page.pageNumber} ---</div>\n`
|
|
272
|
+
: '';
|
|
273
|
+
// Convert text content to HTML paragraphs
|
|
274
|
+
const paragraphs = page.textContent
|
|
275
|
+
.split('\n\n')
|
|
276
|
+
.filter(p => p.trim())
|
|
277
|
+
.map(p => {
|
|
278
|
+
const trimmed = p.trim();
|
|
279
|
+
// Try to detect headings (lines that are short and don't end with punctuation)
|
|
280
|
+
if (trimmed.length < 100 && !trimmed.match(/[.,:;!?]$/)) {
|
|
281
|
+
// Could be a heading - wrap in strong for emphasis
|
|
282
|
+
// The ConversionEngine will handle proper heading detection
|
|
283
|
+
return `<p><strong>${escapeHtml(trimmed)}</strong></p>`;
|
|
284
|
+
}
|
|
285
|
+
return `<p>${escapeHtml(trimmed)}</p>`;
|
|
286
|
+
})
|
|
287
|
+
.join('\n');
|
|
288
|
+
return pageMarker + paragraphs;
|
|
289
|
+
}).join('\n\n');
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Get the number of pages in a PDF without full parsing
|
|
293
|
+
*
|
|
294
|
+
* @param arrayBuffer - The PDF file content as an ArrayBuffer
|
|
295
|
+
* @returns Promise resolving to the page count
|
|
296
|
+
*/
|
|
297
|
+
async getPageCount(arrayBuffer) {
|
|
298
|
+
const pdfjs = await this.ensurePdfjsLoaded();
|
|
299
|
+
const loadingTask = pdfjs.getDocument({
|
|
300
|
+
data: arrayBuffer,
|
|
301
|
+
disableAutoFetch: true,
|
|
302
|
+
disableStream: true,
|
|
303
|
+
});
|
|
304
|
+
try {
|
|
305
|
+
const pdf = await loadingTask.promise;
|
|
306
|
+
return pdf.numPages;
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
try {
|
|
310
|
+
loadingTask.destroy();
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
// Ignore cleanup errors
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Singleton instance for convenience
|
|
320
|
+
*/
|
|
321
|
+
export const pdfAdapter = new PdfAdapter();
|
|
322
|
+
//# sourceMappingURL=pdf-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-adapter.js","sourceRoot":"","sources":["../../../../../../src/shared/adapters/pdf-adapter.ts"],"names":[],"mappings":"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"}
|