officeparser 7.0.3 → 7.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.
Files changed (45) hide show
  1. package/README.md +152 -18
  2. package/dist/OfficeGenerator.d.ts +1 -1
  3. package/dist/OfficeGenerator.js +16 -7
  4. package/dist/OfficeParser.js +6 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.js +12 -3
  7. package/dist/defaults.js +27 -1
  8. package/dist/generators/BaseGenerator.d.ts +3 -3
  9. package/dist/generators/ChunkingGenerator.js +31 -4
  10. package/dist/generators/CsvGenerator.d.ts +1 -1
  11. package/dist/generators/HtmlGenerator.d.ts +2 -1
  12. package/dist/generators/HtmlGenerator.js +462 -40
  13. package/dist/generators/MarkdownGenerator.d.ts +1 -1
  14. package/dist/generators/MarkdownGenerator.js +3 -1
  15. package/dist/generators/PdfGenerator.d.ts +1 -1
  16. package/dist/generators/PdfGenerator.js +51 -10
  17. package/dist/generators/RtfGenerator.d.ts +2 -1
  18. package/dist/generators/RtfGenerator.js +43 -6
  19. package/dist/generators/TextGenerator.d.ts +1 -1
  20. package/dist/officeparser.browser.d.ts +377 -53
  21. package/dist/officeparser.browser.iife.js +380 -93
  22. package/dist/officeparser.browser.mjs +380 -93
  23. package/dist/parsers/CsvParser.js +6 -1
  24. package/dist/parsers/ExcelParser.js +69 -21
  25. package/dist/parsers/HtmlParser.js +15 -1
  26. package/dist/parsers/MarkdownParser.js +18 -10
  27. package/dist/parsers/OpenOfficeParser.js +61 -34
  28. package/dist/parsers/PdfParser.js +26 -1
  29. package/dist/parsers/PowerPointParser.js +168 -40
  30. package/dist/parsers/RtfParser.js +30 -24
  31. package/dist/parsers/WordParser.js +158 -11
  32. package/dist/sbom.cdx.json +100 -100
  33. package/dist/types.d.ts +383 -53
  34. package/dist/types.js +4 -0
  35. package/dist/utils/astUtils.d.ts +2 -2
  36. package/dist/utils/astUtils.js +2 -1
  37. package/dist/utils/configUtils.d.ts +5 -0
  38. package/dist/utils/configUtils.js +69 -2
  39. package/dist/utils/errorUtils.d.ts +20 -0
  40. package/dist/utils/errorUtils.js +39 -3
  41. package/dist/utils/moduleLoader.js +3 -3
  42. package/dist/utils/ocrUtils.js +271 -66
  43. package/dist/utils/xmlUtils.d.ts +17 -0
  44. package/dist/utils/xmlUtils.js +85 -1
  45. package/package.json +3 -2
@@ -37,7 +37,7 @@ export declare class MarkdownGenerator extends BaseGenerator<'md'> {
37
37
  *
38
38
  * @returns A Markdown string
39
39
  */
40
- generate(): Promise<ConversionResult>;
40
+ generate(): Promise<ConversionResult<'md'>>;
41
41
  /**
42
42
  * Recursively processes nodes and builds output.
43
43
  * Overridden to provide AST optimization (merging adjacent text nodes).
@@ -136,7 +136,9 @@ class MarkdownGenerator extends BaseGenerator_js_1.BaseGenerator {
136
136
  else if (this.config.generateIds) {
137
137
  id = ` {#${this.slugify(this.getNodeText(node))}}`;
138
138
  }
139
- const anchors = remainingAnchors.map(aid => `<a name="${aid}"></a>`).join('');
139
+ const anchors = this.config.mdConfig.fallbackToHtml
140
+ ? remainingAnchors.map(aid => `<a name="${aid}"></a>`).join('')
141
+ : '';
140
142
  let content = `${prefix}${childrenOutput}${id}`;
141
143
  // Alignment fallback via HTML div/p
142
144
  if (this.config.mdConfig.fallbackToHtml && meta?.alignment && meta.alignment !== 'left') {
@@ -9,7 +9,7 @@ import { BaseGenerator } from './BaseGenerator.js';
9
9
  */
10
10
  export declare class PdfGenerator extends BaseGenerator<'pdf'> {
11
11
  constructor(ast: OfficeParserAST, config?: GeneratorConfig<'pdf'>);
12
- generate(): Promise<ConversionResult>;
12
+ generate(): Promise<ConversionResult<'pdf'>>;
13
13
  /**
14
14
  * Node.js implementation using Puppeteer.
15
15
  * Uses dynamic import to avoid bundling puppeteer into the library core.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PdfGenerator = void 0;
4
4
  const types_js_1 = require("../types.js");
5
5
  const envUtils_js_1 = require("../utils/envUtils.js");
6
+ const errorUtils_js_1 = require("../utils/errorUtils.js");
6
7
  const BaseGenerator_js_1 = require("./BaseGenerator.js");
7
8
  const HtmlGenerator_js_1 = require("./HtmlGenerator.js");
8
9
  /**
@@ -22,12 +23,6 @@ class PdfGenerator extends BaseGenerator_js_1.BaseGenerator {
22
23
  const htmlGenerator = new HtmlGenerator_js_1.HtmlGenerator(this.ast, {
23
24
  ...this.config,
24
25
  htmlConfig: { ...this.config.htmlConfig, standalone: true },
25
- mdConfig: undefined,
26
- chunksConfig: undefined,
27
- csvConfig: undefined,
28
- textConfig: undefined,
29
- pdfConfig: undefined,
30
- rtfConfig: undefined,
31
26
  });
32
27
  const htmlResult = await htmlGenerator.generate();
33
28
  const html = typeof htmlResult.value === 'string' ? htmlResult.value : '';
@@ -44,6 +39,24 @@ class PdfGenerator extends BaseGenerator_js_1.BaseGenerator {
44
39
  * Uses dynamic import to avoid bundling puppeteer into the library core.
45
40
  */
46
41
  async generateInNode(html) {
42
+ const signal = this.config.abortSignal;
43
+ if (signal?.aborted) {
44
+ throw (0, errorUtils_js_1.getAbortError)();
45
+ }
46
+ let browser;
47
+ const onAbort = async () => {
48
+ if (browser) {
49
+ try {
50
+ await browser.close();
51
+ }
52
+ catch (e) {
53
+ // ignore
54
+ }
55
+ }
56
+ };
57
+ if (signal) {
58
+ signal.addEventListener('abort', onAbort);
59
+ }
47
60
  try {
48
61
  // Dynamic import for peer dependency
49
62
  // @ts-ignore
@@ -70,11 +83,16 @@ class PdfGenerator extends BaseGenerator_js_1.BaseGenerator {
70
83
  // to ensure transparency about the environment state. Programmatically fixing this
71
84
  // would require force-downloading a ~300MB arm64 browser binary or switching to
72
85
  // a system-installed Chrome, both of which are too intrusive for a library.
73
- const browser = await puppeteer.launch(launchOptions);
86
+ browser = await puppeteer.launch(launchOptions);
74
87
  const page = await browser.newPage();
88
+ const pdfConfig = this.config.pdfConfig;
89
+ const timeout = pdfConfig.timeout;
90
+ if (timeout !== undefined && timeout > 0) {
91
+ page.setDefaultTimeout(timeout);
92
+ page.setDefaultNavigationTimeout(timeout);
93
+ }
75
94
  // Set content and wait for network/assets to load
76
95
  await page.setContent(html, { waitUntil: 'networkidle0' });
77
- const pdfConfig = this.config.pdfConfig;
78
96
  const pdfBuffer = await page.pdf({
79
97
  format: pdfConfig.format,
80
98
  width: pdfConfig.width,
@@ -88,18 +106,41 @@ class PdfGenerator extends BaseGenerator_js_1.BaseGenerator {
88
106
  footerTemplate: pdfConfig.footerTemplate,
89
107
  });
90
108
  await browser.close();
109
+ browser = null;
91
110
  return {
92
111
  value: new Uint8Array(pdfBuffer),
93
112
  messages: this.messages
94
113
  };
95
114
  }
96
115
  catch (err) {
97
- this.warn(types_js_1.OfficeWarningType.DEPENDENCY_LOAD_FAILED, `puppeteer. Please install it with 'npm install puppeteer'. Error: ${err.message}`);
116
+ if (browser) {
117
+ try {
118
+ await browser.close();
119
+ }
120
+ catch (e) {
121
+ // ignore
122
+ }
123
+ browser = null;
124
+ }
125
+ if (signal?.aborted) {
126
+ throw (0, errorUtils_js_1.getAbortError)();
127
+ }
128
+ if (err.message && (err.message.includes('timeout') || err.message.includes('Timeout'))) {
129
+ this.warn(types_js_1.OfficeWarningType.PAGE_LOAD_FAILED, `PDF generation timed out: ${err.message}`);
130
+ }
131
+ else {
132
+ this.warn(types_js_1.OfficeWarningType.DEPENDENCY_LOAD_FAILED, `puppeteer. Please install it with 'npm install puppeteer'. Error: ${err.message}`);
133
+ }
98
134
  return {
99
- value: '',
135
+ value: new Uint8Array(),
100
136
  messages: this.messages
101
137
  };
102
138
  }
139
+ finally {
140
+ if (signal) {
141
+ signal.removeEventListener('abort', onAbort);
142
+ }
143
+ }
103
144
  }
104
145
  /**
105
146
  * Browser implementation using hidden iframe and native print.
@@ -7,9 +7,10 @@ export declare class RtfGenerator extends BaseGenerator<'rtf'> {
7
7
  private colorTable;
8
8
  private inTable;
9
9
  constructor(ast: OfficeParserAST, config?: GeneratorConfig<'rtf'>);
10
- generate(): Promise<ConversionResult>;
10
+ generate(): Promise<ConversionResult<'rtf'>>;
11
11
  protected processNodeRecursive(node: OfficeContentNode, processor: (node: OfficeContentNode, childrenOutput: string) => Promise<string>): Promise<string>;
12
12
  private renderBody;
13
13
  private getColorIndex;
14
+ private isLightColor;
14
15
  private escapeRtf;
15
16
  }
@@ -15,7 +15,7 @@ class RtfGenerator extends BaseGenerator_js_1.BaseGenerator {
15
15
  this.colorTable = [];
16
16
  // We first process all nodes to collect colors and analyze structure
17
17
  const bodyContent = await this.renderBody(this.ast);
18
- let output = '{\\rtf1\\ansi\\deff0\n';
18
+ let output = '{\\rtf1\\ansi\\uc1\\deff0\n';
19
19
  // 1. Info Group (Metadata)
20
20
  if (this.config.renderMetadata && this.ast.metadata) {
21
21
  output += '{\\info';
@@ -100,16 +100,34 @@ class RtfGenerator extends BaseGenerator_js_1.BaseGenerator {
100
100
  suffix = '\\strike0 ' + suffix;
101
101
  }
102
102
  if (f.color) {
103
- const idx = this.getColorIndex(f.color);
104
- prefix += `\\cf${idx + 1} `;
103
+ // RTF default background is white. Ensure light text without a dark background remains readable.
104
+ const isTextLight = this.isLightColor(f.color);
105
+ const isBgLight = !f.backgroundColor || this.isLightColor(f.backgroundColor);
106
+ if (!(isTextLight && isBgLight)) {
107
+ const idx = this.getColorIndex(f.color);
108
+ prefix += `\\cf${idx + 1} `;
109
+ }
105
110
  }
106
111
  if (f.backgroundColor) {
107
112
  const idx = this.getColorIndex(f.backgroundColor);
108
113
  prefix += `\\highlight${idx + 1} `;
109
114
  }
110
115
  if (f.size) {
111
- const pt = parseInt(f.size);
112
- prefix += `\\fs${pt * 2} `;
116
+ let pt = 12; // default
117
+ const val = parseFloat(f.size);
118
+ if (!isNaN(val)) {
119
+ if (f.size.includes('in'))
120
+ pt = val * 72;
121
+ else if (f.size.includes('cm'))
122
+ pt = val * 28.3465;
123
+ else if (f.size.includes('mm'))
124
+ pt = val * 2.83465;
125
+ else if (f.size.includes('px'))
126
+ pt = val * 0.75;
127
+ else
128
+ pt = val;
129
+ }
130
+ prefix += `\\fs${Math.round(pt * 2)} `;
113
131
  }
114
132
  text = `{\\f0 ${prefix}${text}${suffix}}`;
115
133
  }
@@ -214,13 +232,32 @@ class RtfGenerator extends BaseGenerator_js_1.BaseGenerator {
214
232
  }
215
233
  return idx;
216
234
  }
235
+ isLightColor(hex) {
236
+ if (!hex || hex.length !== 7 || !hex.startsWith('#'))
237
+ return false;
238
+ const r = parseInt(hex.substring(1, 3), 16);
239
+ const g = parseInt(hex.substring(3, 5), 16);
240
+ const b = parseInt(hex.substring(5, 7), 16);
241
+ if (isNaN(r) || isNaN(g) || isNaN(b))
242
+ return false;
243
+ // Simple luminance calculation
244
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
245
+ return luminance > 0.8;
246
+ }
217
247
  escapeRtf(text) {
218
248
  return text
219
249
  .replace(/\\/g, '\\\\')
220
250
  .replace(/{/g, '\\{')
221
251
  .replace(/}/g, '\\}')
222
252
  .replace(/[^\x00-\x7F]/g, (match) => {
223
- return `\\u${match.charCodeAt(0)}?`;
253
+ let code = match.charCodeAt(0);
254
+ if (code < 256) {
255
+ return `\\'${code.toString(16).padStart(2, '0')}`;
256
+ }
257
+ if (code > 32767) {
258
+ code -= 65536;
259
+ }
260
+ return `{\\uc0\\u${code}}`;
224
261
  });
225
262
  }
226
263
  }
@@ -8,6 +8,6 @@ export declare class TextGenerator extends BaseGenerator<'text'> {
8
8
  /**
9
9
  * Generates plain text by concatenating text content from nodes.
10
10
  */
11
- generate(): Promise<ConversionResult>;
11
+ generate(): Promise<ConversionResult<'text'>>;
12
12
  private renderTable;
13
13
  }