@gmickel/gno 1.45.1 → 2.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/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +46 -0
- package/assets/skill/SKILL.md +7 -6
- package/assets/skill/cli-reference.md +14 -6
- package/assets/skill/mcp-reference.md +4 -1
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +32 -33
- package/bunfig.toml +2 -0
- package/package.json +40 -26
- package/spec/cli.md +30 -11
- package/spec/db/schema.sql +146 -1
- package/spec/mcp.md +26 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +2 -0
- package/src/cli/commands/ask.ts +6 -1
- package/src/cli/commands/daemon.ts +21 -8
- package/src/cli/commands/embed.ts +77 -41
- package/src/cli/commands/mcp/install.ts +20 -0
- package/src/cli/commands/mcp/paths.ts +25 -0
- package/src/cli/commands/mcp/status.ts +6 -0
- package/src/cli/detach.ts +3 -2
- package/src/cli/program.ts +6 -0
- package/src/config/types.ts +3 -3
- package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
- package/src/converters/adapters/officeparser/adapter.ts +1 -2
- package/src/converters/versions.ts +6 -8
- package/src/core/context-evidence.ts +8 -4
- package/src/core/job-manager.ts +95 -13
- package/src/core/network-boundary-inventory.ts +10 -0
- package/src/core/shutdown-budget.ts +45 -0
- package/src/embed/backlog.ts +107 -4
- package/src/embed/batch.ts +42 -2
- package/src/embed/fingerprint.ts +16 -0
- package/src/embed/retry.ts +113 -5
- package/src/embed/variant-backlog.ts +105 -0
- package/src/embed/variant-plan.ts +62 -0
- package/src/embed/variant-retry.ts +113 -0
- package/src/ingestion/graph-reconciliation.ts +327 -0
- package/src/ingestion/sync.ts +9 -272
- package/src/llm/http-inference.ts +6 -0
- package/src/llm/httpEmbedding.ts +37 -6
- package/src/llm/httpGeneration.ts +18 -3
- package/src/llm/httpRerank.ts +23 -5
- package/src/llm/inference-cancellation.ts +168 -0
- package/src/llm/inference-scope.ts +202 -0
- package/src/llm/lazy-ports.ts +115 -0
- package/src/llm/native-worker/client.ts +541 -0
- package/src/llm/native-worker/dispatcher.ts +228 -0
- package/src/llm/native-worker/embedding-identity.ts +33 -0
- package/src/llm/native-worker/entry.ts +173 -0
- package/src/llm/native-worker/errors.ts +32 -0
- package/src/llm/native-worker/evaluation.ts +16 -0
- package/src/llm/native-worker/owned-exit.ts +108 -0
- package/src/llm/native-worker/owner.ts +141 -0
- package/src/llm/native-worker/ports.ts +317 -0
- package/src/llm/native-worker/protocol.ts +442 -0
- package/src/llm/native-worker/runtime-config.ts +92 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
- package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
- package/src/llm/nodeLlamaCpp/generation.ts +34 -5
- package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
- package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
- package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
- package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
- package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
- package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
- package/src/llm/types.ts +35 -5
- package/src/mcp/context.ts +27 -0
- package/src/mcp/http-transport.ts +12 -10
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-profile.ts +30 -8
- package/src/mcp/tools/context.ts +8 -11
- package/src/mcp/tools/embed.ts +1 -1
- package/src/mcp/tools/index-cmd.ts +1 -1
- package/src/mcp/tools/index.ts +10 -8
- package/src/mcp/tools/query.ts +14 -30
- package/src/mcp/tools/vsearch.ts +1 -1
- package/src/pipeline/answer.ts +23 -3
- package/src/pipeline/claim-verifier.ts +6 -0
- package/src/pipeline/expansion.ts +43 -40
- package/src/pipeline/explain.ts +6 -2
- package/src/pipeline/filters.ts +63 -0
- package/src/pipeline/fusion.ts +29 -9
- package/src/pipeline/graph-retrieval.ts +29 -9
- package/src/pipeline/hybrid.ts +198 -55
- package/src/pipeline/hydration.ts +161 -0
- package/src/pipeline/owner-fusion.ts +87 -0
- package/src/pipeline/rerank.ts +35 -11
- package/src/pipeline/search.ts +13 -2
- package/src/pipeline/types.ts +5 -3
- package/src/pipeline/vsearch.ts +87 -7
- package/src/sdk/client.ts +47 -3
- package/src/sdk/embed.ts +63 -39
- package/src/serve/background-runtime.ts +1 -1
- package/src/serve/context.ts +41 -56
- package/src/serve/embed-scheduler.ts +58 -35
- package/src/serve/public/components/IndexingProgress.tsx +46 -60
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/shiki-language-ids.ts +14 -0
- package/src/serve/resident-admission.ts +36 -36
- package/src/serve/resident-background-work.ts +20 -2
- package/src/serve/resident-request.ts +11 -5
- package/src/serve/resident-runtime.ts +97 -61
- package/src/serve/resident-shutdown.ts +153 -0
- package/src/serve/routes/api.ts +3 -1
- package/src/serve/server.ts +47 -26
- package/src/store/migrations/028-vector-variants.ts +54 -0
- package/src/store/migrations/029-graph-reference-state.ts +77 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +251 -183
- package/src/store/sqlite/eligibility.ts +174 -0
- package/src/store/sqlite/graph-edge-application.ts +66 -0
- package/src/store/sqlite/graph-reference-state.ts +194 -0
- package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
- package/src/store/types.ts +80 -12
- package/src/store/vector/eligibility.ts +36 -0
- package/src/store/vector/freshness.ts +33 -6
- package/src/store/vector/lazy.ts +81 -0
- package/src/store/vector/sqlite-vec.ts +106 -54
- package/src/store/vector/stats.ts +14 -3
- package/src/store/vector/types.ts +35 -2
- package/src/store/vector/variant-search.ts +192 -0
- package/src/store/vector/variants.ts +451 -0
- package/vendor/converters/markitdown-ts/LICENSE +21 -0
- package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
- package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
- package/vendor/converters/markitdown-ts/package.json +77 -0
- package/vendor/converters/officeparser/LICENSE +21 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
- package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
- package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
- package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
- package/vendor/converters/officeparser/dist/cli.js +381 -0
- package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
- package/vendor/converters/officeparser/dist/defaults.js +218 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
- package/vendor/converters/officeparser/dist/index.d.ts +60 -0
- package/vendor/converters/officeparser/dist/index.js +72 -0
- package/vendor/converters/officeparser/dist/index.mjs +18 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
- package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
- package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
- package/vendor/converters/officeparser/dist/types.js +107 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
- package/vendor/converters/officeparser/package.json +147 -0
- package/vendor/converters/upstream-manifest.json +124 -0
- package/vendor/dependency-fixes/README.md +77 -0
- package/vendor/dependency-fixes/vendor-converters.py +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-627emwpj.js +0 -75
- /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ConversionResult, GeneratorConfig, OfficeParserAST } from '../types.js';
|
|
2
|
+
import { BaseGenerator } from './BaseGenerator.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates high-fidelity PDF documents using a headless browser engine.
|
|
5
|
+
*
|
|
6
|
+
* Uses an environment-aware strategy:
|
|
7
|
+
* - Node.js: Uses Puppeteer (peer dependency) for server-side rendering.
|
|
8
|
+
* - Browser: Leverages native browser print capabilities.
|
|
9
|
+
*/
|
|
10
|
+
export declare class PdfGenerator extends BaseGenerator<'pdf'> {
|
|
11
|
+
constructor(ast: OfficeParserAST, config?: GeneratorConfig<'pdf'>);
|
|
12
|
+
generate(): Promise<ConversionResult<'pdf'>>;
|
|
13
|
+
/**
|
|
14
|
+
* Node.js implementation using Puppeteer.
|
|
15
|
+
* Uses dynamic import to avoid bundling puppeteer into the library core.
|
|
16
|
+
*/
|
|
17
|
+
private generateInNode;
|
|
18
|
+
/**
|
|
19
|
+
* Browser implementation using hidden iframe and native print.
|
|
20
|
+
*/
|
|
21
|
+
private generateInBrowser;
|
|
22
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PdfGenerator = void 0;
|
|
4
|
+
const types_js_1 = require("../types.js");
|
|
5
|
+
const envUtils_js_1 = require("../utils/envUtils.js");
|
|
6
|
+
const errorUtils_js_1 = require("../utils/errorUtils.js");
|
|
7
|
+
const BaseGenerator_js_1 = require("./BaseGenerator.js");
|
|
8
|
+
const HtmlGenerator_js_1 = require("./HtmlGenerator.js");
|
|
9
|
+
/**
|
|
10
|
+
* Generates high-fidelity PDF documents using a headless browser engine.
|
|
11
|
+
*
|
|
12
|
+
* Uses an environment-aware strategy:
|
|
13
|
+
* - Node.js: Uses Puppeteer (peer dependency) for server-side rendering.
|
|
14
|
+
* - Browser: Leverages native browser print capabilities.
|
|
15
|
+
*/
|
|
16
|
+
class PdfGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
17
|
+
constructor(ast, config) {
|
|
18
|
+
super('pdf', ast, config);
|
|
19
|
+
}
|
|
20
|
+
async generate() {
|
|
21
|
+
// Step 1: Generate high-fidelity HTML as the source for PDF rendering
|
|
22
|
+
// We reuse the current configuration but ensure standalone mode is on for HTML
|
|
23
|
+
const htmlGenerator = new HtmlGenerator_js_1.HtmlGenerator(this.ast, {
|
|
24
|
+
...this.config,
|
|
25
|
+
// Force sourceAttributes off: those data-* attributes are wire-format plumbing for
|
|
26
|
+
// structured consumers and change the mermaid shape's rendered appearance, neither of
|
|
27
|
+
// which belongs in a printed PDF.
|
|
28
|
+
htmlConfig: { ...this.config.htmlConfig, standalone: true, sourceAttributes: false },
|
|
29
|
+
});
|
|
30
|
+
const htmlResult = await htmlGenerator.generate();
|
|
31
|
+
const html = typeof htmlResult.value === 'string' ? htmlResult.value : '';
|
|
32
|
+
// Step 2: Render to PDF based on environment
|
|
33
|
+
if (envUtils_js_1.isBrowser) {
|
|
34
|
+
return this.generateInBrowser(html);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return this.generateInNode(html);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Node.js implementation using Puppeteer.
|
|
42
|
+
* Uses dynamic import to avoid bundling puppeteer into the library core.
|
|
43
|
+
*/
|
|
44
|
+
async generateInNode(html) {
|
|
45
|
+
const signal = this.config.abortSignal;
|
|
46
|
+
if (signal?.aborted) {
|
|
47
|
+
throw (0, errorUtils_js_1.getAbortError)();
|
|
48
|
+
}
|
|
49
|
+
let browser;
|
|
50
|
+
const onAbort = async () => {
|
|
51
|
+
if (browser) {
|
|
52
|
+
try {
|
|
53
|
+
await browser.close();
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
// ignore
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
if (signal) {
|
|
61
|
+
signal.addEventListener('abort', onAbort);
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
// Dynamic import for peer dependency
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
const puppeteerModule = await import('puppeteer');
|
|
67
|
+
const puppeteer = puppeteerModule.default || puppeteerModule;
|
|
68
|
+
const launchOptions = { ...this.config.pdfConfig.launchOptions };
|
|
69
|
+
// Handle Apple Silicon / Rosetta performance warning and binary detection
|
|
70
|
+
const isMac = process.platform === 'darwin';
|
|
71
|
+
const isX64 = process.arch === 'x64';
|
|
72
|
+
let isRosetta = false;
|
|
73
|
+
if (isMac && isX64) {
|
|
74
|
+
try {
|
|
75
|
+
const { execSync } = await import('child_process');
|
|
76
|
+
isRosetta = execSync('sysctl -n hw.optional.arm64', { stdio: 'pipe' }).toString().trim() === '1';
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
// Ignore errors in detection
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (isRosetta) {
|
|
83
|
+
this.warn(types_js_1.OfficeWarningType.PERFORMANCE_TIP, "You are running on Apple Silicon using an x64 Node.js installation. PDF generation will be significantly faster (avoiding Rosetta translation) if you switch to a native arm64 Node.js version.");
|
|
84
|
+
}
|
|
85
|
+
// Note: We are no longer suppressing the Puppeteer 'Degraded performance' warning here
|
|
86
|
+
// to ensure transparency about the environment state. Programmatically fixing this
|
|
87
|
+
// would require force-downloading a ~300MB arm64 browser binary or switching to
|
|
88
|
+
// a system-installed Chrome, both of which are too intrusive for a library.
|
|
89
|
+
browser = await puppeteer.launch(launchOptions);
|
|
90
|
+
const page = await browser.newPage();
|
|
91
|
+
// Harden against SSRF: the HTML being rendered is derived from an untrusted
|
|
92
|
+
// document, and `networkidle0` would otherwise fetch every URL it references
|
|
93
|
+
// (external images, stylesheets, etc.) from this host — reaching internal
|
|
94
|
+
// services or a cloud metadata endpoint (169.254.169.254). Intercept requests
|
|
95
|
+
// and allow only inline data/blob URIs and the configured chart CDN; abort every
|
|
96
|
+
// other remote fetch.
|
|
97
|
+
const allowedHosts = new Set();
|
|
98
|
+
try {
|
|
99
|
+
const chartSrc = this.config.htmlConfig?.chartJsSrc;
|
|
100
|
+
if (chartSrc)
|
|
101
|
+
allowedHosts.add(new URL(chartSrc).host);
|
|
102
|
+
}
|
|
103
|
+
catch { /* no or invalid chart CDN configured */ }
|
|
104
|
+
let blockedRemoteResource = false;
|
|
105
|
+
await page.setRequestInterception(true);
|
|
106
|
+
page.on('request', (req) => {
|
|
107
|
+
const url = req.url();
|
|
108
|
+
if (url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('about:')) {
|
|
109
|
+
return req.continue().catch(() => { });
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
if (allowedHosts.has(new URL(url).host)) {
|
|
113
|
+
return req.continue().catch(() => { });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch { /* unparseable URL — fall through and block */ }
|
|
117
|
+
blockedRemoteResource = true;
|
|
118
|
+
return req.abort().catch(() => { });
|
|
119
|
+
});
|
|
120
|
+
const pdfConfig = this.config.pdfConfig;
|
|
121
|
+
const timeout = pdfConfig.timeout;
|
|
122
|
+
if (timeout !== undefined && timeout > 0) {
|
|
123
|
+
page.setDefaultTimeout(timeout);
|
|
124
|
+
page.setDefaultNavigationTimeout(timeout);
|
|
125
|
+
}
|
|
126
|
+
// Set content and wait for network/assets to load
|
|
127
|
+
await page.setContent(html, { waitUntil: 'networkidle0' });
|
|
128
|
+
const pdfBuffer = await page.pdf({
|
|
129
|
+
format: pdfConfig.format,
|
|
130
|
+
width: pdfConfig.width,
|
|
131
|
+
height: pdfConfig.height,
|
|
132
|
+
landscape: pdfConfig.landscape,
|
|
133
|
+
printBackground: pdfConfig.printBackground,
|
|
134
|
+
scale: pdfConfig.scale,
|
|
135
|
+
margin: pdfConfig.margin,
|
|
136
|
+
displayHeaderFooter: pdfConfig.displayHeaderFooter,
|
|
137
|
+
headerTemplate: pdfConfig.headerTemplate,
|
|
138
|
+
footerTemplate: pdfConfig.footerTemplate,
|
|
139
|
+
});
|
|
140
|
+
await browser.close();
|
|
141
|
+
browser = null;
|
|
142
|
+
if (blockedRemoteResource) {
|
|
143
|
+
this.warn(types_js_1.OfficeWarningType.BROWSER_GENERATION_LIMITATION, 'One or more remote resources referenced by the document were blocked during PDF rendering to prevent server-side request forgery (SSRF). Only inline images and the configured chart CDN are loaded.');
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
value: new Uint8Array(pdfBuffer),
|
|
147
|
+
messages: this.messages
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (browser) {
|
|
152
|
+
try {
|
|
153
|
+
await browser.close();
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
// ignore
|
|
157
|
+
}
|
|
158
|
+
browser = null;
|
|
159
|
+
}
|
|
160
|
+
if (signal?.aborted) {
|
|
161
|
+
throw (0, errorUtils_js_1.getAbortError)();
|
|
162
|
+
}
|
|
163
|
+
if (err.message && (err.message.includes('timeout') || err.message.includes('Timeout'))) {
|
|
164
|
+
this.warn(types_js_1.OfficeWarningType.PAGE_LOAD_FAILED, `PDF generation timed out: ${err.message}`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this.warn(types_js_1.OfficeWarningType.DEPENDENCY_LOAD_FAILED, `puppeteer. Please install it with 'npm install puppeteer'. Error: ${err.message}`);
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
value: new Uint8Array(),
|
|
171
|
+
messages: this.messages
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
if (signal) {
|
|
176
|
+
signal.removeEventListener('abort', onAbort);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Browser implementation using hidden iframe and native print.
|
|
182
|
+
*/
|
|
183
|
+
async generateInBrowser(html) {
|
|
184
|
+
this.warn(types_js_1.OfficeWarningType.BROWSER_GENERATION_LIMITATION, "Browser-based PDF generation triggered. For automated 'Save as PDF' without user interaction, we recommend using 'html2pdf.js' as a custom generator hook.");
|
|
185
|
+
// In a browser environment, we return the HTML and suggest using window.print()
|
|
186
|
+
// Or we could trigger a print dialog immediately if desired,
|
|
187
|
+
// but returning the string allows the user to decide where to inject it.
|
|
188
|
+
return {
|
|
189
|
+
value: html,
|
|
190
|
+
messages: this.messages
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.PdfGenerator = PdfGenerator;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ConversionResult, GeneratorConfig, OfficeContentNode, OfficeParserAST } from '../types.js';
|
|
2
|
+
import { BaseGenerator } from './BaseGenerator.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates high-fidelity RTF (Rich Text Format) from an AST.
|
|
5
|
+
*/
|
|
6
|
+
export declare class RtfGenerator extends BaseGenerator<'rtf'> {
|
|
7
|
+
private colorTable;
|
|
8
|
+
private inTable;
|
|
9
|
+
/**
|
|
10
|
+
* Set while rendering a heading's children.
|
|
11
|
+
*
|
|
12
|
+
* A heading emits its own `{\\b\\fs44 ...}` wrapper, so a run inside it that also carries bold
|
|
13
|
+
* and a size - which is now the normal case for ODF, where the heading's paragraph style is
|
|
14
|
+
* inherited by its runs - would emit a nested `\\fs28` that *overrides* the outer `\\fs44`.
|
|
15
|
+
* The heading then renders at the body-text size it was styled with rather than at heading
|
|
16
|
+
* size. Suppressing the inherited weight and size inside a heading keeps the heading's own
|
|
17
|
+
* wrapper authoritative; every other property (colour, font) still comes through.
|
|
18
|
+
*/
|
|
19
|
+
private inHeading;
|
|
20
|
+
/** As `inHeading`, but for the inherited font size - see `hasUniformFormatting`. */
|
|
21
|
+
private headingUniformSize;
|
|
22
|
+
constructor(ast: OfficeParserAST, config?: GeneratorConfig<'rtf'>);
|
|
23
|
+
generate(): Promise<ConversionResult<'rtf'>>;
|
|
24
|
+
protected processNodeRecursive(node: OfficeContentNode, processor: (node: OfficeContentNode, childrenOutput: string) => Promise<string>): Promise<string>;
|
|
25
|
+
private renderBody;
|
|
26
|
+
private getColorIndex;
|
|
27
|
+
private isLightColor;
|
|
28
|
+
private escapeRtf;
|
|
29
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RtfGenerator = void 0;
|
|
4
|
+
const sanitize_js_1 = require("../utils/sanitize.js");
|
|
5
|
+
const BaseGenerator_js_1 = require("./BaseGenerator.js");
|
|
6
|
+
const errorUtils_js_1 = require("../utils/errorUtils.js");
|
|
7
|
+
/**
|
|
8
|
+
* Generates high-fidelity RTF (Rich Text Format) from an AST.
|
|
9
|
+
*/
|
|
10
|
+
class RtfGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
11
|
+
colorTable = [];
|
|
12
|
+
inTable = false;
|
|
13
|
+
/**
|
|
14
|
+
* Set while rendering a heading's children.
|
|
15
|
+
*
|
|
16
|
+
* A heading emits its own `{\\b\\fs44 ...}` wrapper, so a run inside it that also carries bold
|
|
17
|
+
* and a size - which is now the normal case for ODF, where the heading's paragraph style is
|
|
18
|
+
* inherited by its runs - would emit a nested `\\fs28` that *overrides* the outer `\\fs44`.
|
|
19
|
+
* The heading then renders at the body-text size it was styled with rather than at heading
|
|
20
|
+
* size. Suppressing the inherited weight and size inside a heading keeps the heading's own
|
|
21
|
+
* wrapper authoritative; every other property (colour, font) still comes through.
|
|
22
|
+
*/
|
|
23
|
+
inHeading = false;
|
|
24
|
+
/** As `inHeading`, but for the inherited font size - see `hasUniformFormatting`. */
|
|
25
|
+
headingUniformSize = false;
|
|
26
|
+
constructor(ast, config) {
|
|
27
|
+
super('rtf', ast, config);
|
|
28
|
+
}
|
|
29
|
+
async generate() {
|
|
30
|
+
this.colorTable = [];
|
|
31
|
+
// We first process all nodes to collect colors and analyze structure
|
|
32
|
+
const bodyContent = await this.renderBody(this.ast);
|
|
33
|
+
let output = '{\\rtf1\\ansi\\uc1\\deff0\n';
|
|
34
|
+
// 1. Info Group (Metadata)
|
|
35
|
+
const meta = this.effectiveMetadata;
|
|
36
|
+
if (this.config.renderMetadata && meta) {
|
|
37
|
+
// RTF's \info group is a fixed set of control words with no slot for caller-defined keys.
|
|
38
|
+
this.warnUnrepresentableCustomMetadata('RTF');
|
|
39
|
+
output += '{\\info';
|
|
40
|
+
if (meta.title)
|
|
41
|
+
output += `{\\title ${this.escapeRtf(meta.title)}}`;
|
|
42
|
+
if (meta.author)
|
|
43
|
+
output += `{\\author ${this.escapeRtf(meta.author)}}`;
|
|
44
|
+
if (meta.description)
|
|
45
|
+
output += `{\\comm ${this.escapeRtf(meta.description)}}`;
|
|
46
|
+
// \subject and \keywords are standard \info destinations, so unlike a caller's
|
|
47
|
+
// arbitrary custom keys these DO have a home in RTF.
|
|
48
|
+
if (meta.subject)
|
|
49
|
+
output += `{\\subject ${this.escapeRtf(meta.subject)}}`;
|
|
50
|
+
if (meta.keywords)
|
|
51
|
+
output += `{\\keywords ${this.escapeRtf(meta.keywords)}}`;
|
|
52
|
+
output += '}\n';
|
|
53
|
+
}
|
|
54
|
+
// 2. Font Table
|
|
55
|
+
output += '{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Times New Roman;}}\n';
|
|
56
|
+
// 3. Color Table
|
|
57
|
+
if (this.colorTable.length > 0) {
|
|
58
|
+
output += '{\\colortbl;';
|
|
59
|
+
for (const hex of this.colorTable) {
|
|
60
|
+
const r = parseInt(hex.substring(1, 3), 16);
|
|
61
|
+
const g = parseInt(hex.substring(3, 5), 16);
|
|
62
|
+
const b = parseInt(hex.substring(5, 7), 16);
|
|
63
|
+
output += `\\red${r}\\green${g}\\blue${b};`;
|
|
64
|
+
}
|
|
65
|
+
output += '}\n';
|
|
66
|
+
}
|
|
67
|
+
// 4. Body
|
|
68
|
+
output += '\\f0\\fs24\n';
|
|
69
|
+
output += bodyContent;
|
|
70
|
+
output += '}';
|
|
71
|
+
return {
|
|
72
|
+
value: output,
|
|
73
|
+
messages: this.messages
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async processNodeRecursive(node, processor) {
|
|
77
|
+
// Mirrors the check in BaseGenerator.processNodeRecursive. This override replaces that
|
|
78
|
+
// method entirely, so without repeating the check here the signal would be silently
|
|
79
|
+
// inert for this generator - which is exactly how it was missed.
|
|
80
|
+
(0, errorUtils_js_1.checkAbortSignal)(this.config.abortSignal);
|
|
81
|
+
const wasInTable = this.inTable;
|
|
82
|
+
if (node.type === 'table')
|
|
83
|
+
this.inTable = true;
|
|
84
|
+
const wasInHeading = this.inHeading;
|
|
85
|
+
const wasHeadingSize = this.headingUniformSize;
|
|
86
|
+
if (node.type === 'heading') {
|
|
87
|
+
this.inHeading = this.hasUniformFormatting(node, f => f?.bold === true);
|
|
88
|
+
this.headingUniformSize = this.hasUniformFormatting(node, f => !!f?.size);
|
|
89
|
+
}
|
|
90
|
+
const result = await super.processNodeRecursive(node, processor);
|
|
91
|
+
this.inTable = wasInTable;
|
|
92
|
+
this.inHeading = wasInHeading;
|
|
93
|
+
this.headingUniformSize = wasHeadingSize;
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
async renderBody(ast) {
|
|
97
|
+
let body = '';
|
|
98
|
+
this.inTable = false;
|
|
99
|
+
const processor = async (node, childrenOutput) => {
|
|
100
|
+
const mapping = this.getSemanticMapping(node);
|
|
101
|
+
if (mapping) {
|
|
102
|
+
if (mapping.tag === 'blockquote') {
|
|
103
|
+
const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
|
|
104
|
+
return `${pPr}\\li720\\ri720\\sa120 ${childrenOutput}\\par\n`;
|
|
105
|
+
}
|
|
106
|
+
const hMatch = mapping.tag.match(/^h([1-6])$/);
|
|
107
|
+
if (hMatch) {
|
|
108
|
+
const level = parseInt(hMatch[1]);
|
|
109
|
+
const fontSize = 24 + (6 - level) * 4;
|
|
110
|
+
const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
|
|
111
|
+
return `${pPr}\\s${level}\\sb240\\sa120{\\b\\fs${fontSize} ${childrenOutput}}\\par\n`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
switch (node.type) {
|
|
115
|
+
case 'text': {
|
|
116
|
+
let text = this.escapeRtf(node.text || '');
|
|
117
|
+
const f = node.formatting;
|
|
118
|
+
const meta = node.metadata;
|
|
119
|
+
if (this.config.includeFormatting && f) {
|
|
120
|
+
let prefix = '';
|
|
121
|
+
let suffix = '';
|
|
122
|
+
if (f.bold && !this.inHeading) {
|
|
123
|
+
prefix += '\\b ';
|
|
124
|
+
suffix = '\\b0 ' + suffix;
|
|
125
|
+
}
|
|
126
|
+
if (f.italic) {
|
|
127
|
+
prefix += '\\i ';
|
|
128
|
+
suffix = '\\i0 ' + suffix;
|
|
129
|
+
}
|
|
130
|
+
if (f.underline) {
|
|
131
|
+
prefix += '\\ul ';
|
|
132
|
+
suffix = '\\ul0 ' + suffix;
|
|
133
|
+
}
|
|
134
|
+
if (f.strikethrough) {
|
|
135
|
+
prefix += '\\strike ';
|
|
136
|
+
suffix = '\\strike0 ' + suffix;
|
|
137
|
+
}
|
|
138
|
+
if (f.color) {
|
|
139
|
+
// RTF default background is white. Ensure light text without a dark background remains readable.
|
|
140
|
+
const isTextLight = this.isLightColor(f.color);
|
|
141
|
+
const isBgLight = !f.backgroundColor || this.isLightColor(f.backgroundColor);
|
|
142
|
+
if (!(isTextLight && isBgLight)) {
|
|
143
|
+
const idx = this.getColorIndex(f.color);
|
|
144
|
+
prefix += `\\cf${idx + 1} `;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (f.backgroundColor) {
|
|
148
|
+
const idx = this.getColorIndex(f.backgroundColor);
|
|
149
|
+
prefix += `\\highlight${idx + 1} `;
|
|
150
|
+
}
|
|
151
|
+
if (f.size && !this.headingUniformSize) {
|
|
152
|
+
let pt = 12; // default
|
|
153
|
+
const val = parseFloat(f.size);
|
|
154
|
+
if (!isNaN(val)) {
|
|
155
|
+
if (f.size.includes('in'))
|
|
156
|
+
pt = val * 72;
|
|
157
|
+
else if (f.size.includes('cm'))
|
|
158
|
+
pt = val * 28.3465;
|
|
159
|
+
else if (f.size.includes('mm'))
|
|
160
|
+
pt = val * 2.83465;
|
|
161
|
+
else if (f.size.includes('px'))
|
|
162
|
+
pt = val * 0.75;
|
|
163
|
+
else
|
|
164
|
+
pt = val;
|
|
165
|
+
}
|
|
166
|
+
prefix += `\\fs${Math.round(pt * 2)} `;
|
|
167
|
+
}
|
|
168
|
+
text = `{\\f0 ${prefix}${text}${suffix}}`;
|
|
169
|
+
}
|
|
170
|
+
if (meta?.link) {
|
|
171
|
+
const isInternal = meta.linkType !== 'external';
|
|
172
|
+
if (!this.config.ignoreInternalLinks || !isInternal) {
|
|
173
|
+
// Scheme-checked, not merely escaped. escapeRtf neutralizes the field
|
|
174
|
+
// metacharacters but says nothing about where the link points, so RTF
|
|
175
|
+
// was the one generator that would emit `javascript:` or a `file://`
|
|
176
|
+
// /UNC target that HTML and Markdown both reject. On rejection, fall
|
|
177
|
+
// through to the bare link text - the same degradation as HTML's
|
|
178
|
+
// href="" and Markdown's [text]().
|
|
179
|
+
const safeLink = (0, sanitize_js_1.sanitizeRtfUrl)(meta.link);
|
|
180
|
+
if (safeLink) {
|
|
181
|
+
return `{\\field{\\*\\fldinst{HYPERLINK "${safeLink}"}}{\\fldrslt ${text}}}`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return text;
|
|
186
|
+
}
|
|
187
|
+
case 'heading': {
|
|
188
|
+
const meta = node.metadata;
|
|
189
|
+
const level = meta?.level || 1;
|
|
190
|
+
const fontSize = 24 + (6 - level) * 4;
|
|
191
|
+
const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
|
|
192
|
+
return `${pPr}\\s${level}\\sb240\\sa120{\\b\\fs${fontSize} ${childrenOutput}}\\par\n`;
|
|
193
|
+
}
|
|
194
|
+
case 'paragraph': {
|
|
195
|
+
let pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
|
|
196
|
+
pPr += '\\sa120';
|
|
197
|
+
if (this.config.includeFormatting && node.metadata) {
|
|
198
|
+
const meta = node.metadata;
|
|
199
|
+
if (meta.alignment) {
|
|
200
|
+
if (meta.alignment === 'center')
|
|
201
|
+
pPr += '\\qc';
|
|
202
|
+
else if (meta.alignment === 'right')
|
|
203
|
+
pPr += '\\qr';
|
|
204
|
+
else if (meta.alignment === 'justify')
|
|
205
|
+
pPr += '\\qj';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return `${pPr} ${childrenOutput}\\par\n`;
|
|
209
|
+
}
|
|
210
|
+
case 'list': {
|
|
211
|
+
const meta = node.metadata;
|
|
212
|
+
const level = meta?.indentation || 0;
|
|
213
|
+
const indent = (level + 1) * 360;
|
|
214
|
+
const isOrdered = meta?.listType === 'ordered';
|
|
215
|
+
const marker = isOrdered ? `${(meta.itemIndex ?? 0) + 1}. ` : '\\bullet ';
|
|
216
|
+
const listControl = isOrdered ? '\\pndec' : '\\pnbullet';
|
|
217
|
+
const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
|
|
218
|
+
return `${pPr}\\li${indent}\\fi-360\\ilvl${level}${listControl} ${marker}${childrenOutput}\\par\n`;
|
|
219
|
+
}
|
|
220
|
+
case 'table': {
|
|
221
|
+
return `\\pard\\sa0\n${childrenOutput}`;
|
|
222
|
+
}
|
|
223
|
+
case 'row': {
|
|
224
|
+
const cells = node.children || [];
|
|
225
|
+
const pageWidth = 9000; // Standard twips width
|
|
226
|
+
const cellWidth = Math.floor(pageWidth / (cells.length || 1));
|
|
227
|
+
let cellDefs = '';
|
|
228
|
+
for (let i = 0; i < cells.length; i++) {
|
|
229
|
+
// Add basic cell borders and calculate width
|
|
230
|
+
cellDefs += `\\clbrdrt\\brdrs\\brdrw10\\clbrdrl\\brdrs\\brdrw10\\clbrdrb\\brdrs\\brdrw10\\clbrdrr\\brdrs\\brdrw10\\cellx${(i + 1) * cellWidth}`;
|
|
231
|
+
}
|
|
232
|
+
return `\\trowd\\trgaph108\\trleft-108${cellDefs}\n${childrenOutput}\\row\n`;
|
|
233
|
+
}
|
|
234
|
+
case 'cell': {
|
|
235
|
+
return `\\pard\\intbl\\sb60\\sa60 ${childrenOutput}\\cell\n`;
|
|
236
|
+
}
|
|
237
|
+
case 'image': {
|
|
238
|
+
if (!this.config.includeImages)
|
|
239
|
+
return '';
|
|
240
|
+
const meta = node.metadata;
|
|
241
|
+
const attachmentName = meta?.attachmentName;
|
|
242
|
+
const attachment = this.ast.attachments.find(a => a.name === attachmentName);
|
|
243
|
+
if (attachment && attachment.data) {
|
|
244
|
+
const type = attachment.extension === 'png' ? 'pngblip' : 'jpegblip';
|
|
245
|
+
// Convert base64 to hex
|
|
246
|
+
const binary = atob(attachment.data);
|
|
247
|
+
let hex = '';
|
|
248
|
+
for (let i = 0; i < binary.length; i++) {
|
|
249
|
+
const h = binary.charCodeAt(i).toString(16);
|
|
250
|
+
hex += h.length === 1 ? '0' + h : h;
|
|
251
|
+
if (i % 64 === 63)
|
|
252
|
+
hex += '\n'; // Add newlines for better RTF readability
|
|
253
|
+
}
|
|
254
|
+
// Default goals (approx 3 inches wide at 1440 twips per inch)
|
|
255
|
+
return `{\\pict\\${type}\\picwgoal4320\\pichgoal3240\n${hex}\n}\n`;
|
|
256
|
+
}
|
|
257
|
+
return '';
|
|
258
|
+
}
|
|
259
|
+
case 'break': {
|
|
260
|
+
return node.metadata?.breakType === 'page' ? '\\page\n' : '\\line\n';
|
|
261
|
+
}
|
|
262
|
+
case 'embed': {
|
|
263
|
+
// RTF has no embed concept - degrade to the URL as plain text rather than
|
|
264
|
+
// silently dropping the node (it has no children to fall back to).
|
|
265
|
+
const meta = node.metadata;
|
|
266
|
+
if (!meta?.url)
|
|
267
|
+
return '';
|
|
268
|
+
// Rendered as visible text rather than a field, but still a URL a reader may
|
|
269
|
+
// copy, so it gets the same scheme policy.
|
|
270
|
+
const safeUrl = (0, sanitize_js_1.sanitizeRtfUrl)(meta.url);
|
|
271
|
+
if (!safeUrl)
|
|
272
|
+
return '';
|
|
273
|
+
const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
|
|
274
|
+
return `${pPr}\\sa120 ${safeUrl}\\par\n`;
|
|
275
|
+
}
|
|
276
|
+
default:
|
|
277
|
+
return childrenOutput;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
for (const node of ast.content) {
|
|
281
|
+
body += await this.processNodeRecursive(node, processor);
|
|
282
|
+
}
|
|
283
|
+
if (this.collectedNotes.length > 0) {
|
|
284
|
+
body += '\\pard\\sb120\\sa120\\keepn{\\b Notes:}\\par\n';
|
|
285
|
+
for (const note of this.collectedNotes) {
|
|
286
|
+
body += await this.processNodeRecursive(note, processor);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return body;
|
|
290
|
+
}
|
|
291
|
+
getColorIndex(hex) {
|
|
292
|
+
const h = hex.toUpperCase();
|
|
293
|
+
let idx = this.colorTable.indexOf(h);
|
|
294
|
+
if (idx === -1) {
|
|
295
|
+
idx = this.colorTable.length;
|
|
296
|
+
this.colorTable.push(h);
|
|
297
|
+
}
|
|
298
|
+
return idx;
|
|
299
|
+
}
|
|
300
|
+
isLightColor(hex) {
|
|
301
|
+
if (!hex || hex.length !== 7 || !hex.startsWith('#'))
|
|
302
|
+
return false;
|
|
303
|
+
const r = parseInt(hex.substring(1, 3), 16);
|
|
304
|
+
const g = parseInt(hex.substring(3, 5), 16);
|
|
305
|
+
const b = parseInt(hex.substring(5, 7), 16);
|
|
306
|
+
if (isNaN(r) || isNaN(g) || isNaN(b))
|
|
307
|
+
return false;
|
|
308
|
+
// Simple luminance calculation
|
|
309
|
+
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
310
|
+
return luminance > 0.8;
|
|
311
|
+
}
|
|
312
|
+
escapeRtf(text) {
|
|
313
|
+
return (0, sanitize_js_1.escapeRtf)(text);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
exports.RtfGenerator = RtfGenerator;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ConversionResult, GeneratorConfig, OfficeParserAST } from '../types.js';
|
|
2
|
+
import { BaseGenerator } from './BaseGenerator.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates plain text from an AST.
|
|
5
|
+
*/
|
|
6
|
+
export declare class TextGenerator extends BaseGenerator<'text'> {
|
|
7
|
+
constructor(ast: OfficeParserAST, config?: GeneratorConfig<'text'>);
|
|
8
|
+
/**
|
|
9
|
+
* Generates plain text by concatenating text content from nodes.
|
|
10
|
+
*/
|
|
11
|
+
generate(): Promise<ConversionResult<'text'>>;
|
|
12
|
+
private renderTable;
|
|
13
|
+
}
|